Phase 1-3: Add memory, skills, and improvement systems
- memory/: cross-session project memory with decisions, lessons, failures, architecture, and sessions categories. Each has format templates and lifecycle documentation. - skills/: 12 reusable specialized methodologies (tdd, systematic-debugging, architecture-design, code-review, security-review, repository-analysis, failure-analysis, refactoring, test-analysis, incident-investigation, browser-automation, research). Each has frontmatter and methodology sections. - improvements/: proposal-based improvement system requiring human approval. - scripts/memory-lifecycle.sh: deterministic memory operations (recall, store, list, search, sessions, cleanup). - scripts/test-memory-system.sh: 12 structural tests for all new systems. - orchestrator.md: added Memory Recall stage, Learning and Memory Storage stage, Improvement Proposals workflow, memory/skills rules, and 3 new actions (A23-A27) to the action catalog. Updated behavioral acceptance test and state separation model. - All 12 subagents: added Memory & Skills Awareness sections with recall and store instructions. - docs/AGENT_ARCHITECTURE.md: documented memory, skills, and improvements systems (sections 12-14). Updated action count (27), state model, and remaining weaknesses. - README.md: documented new systems, updated repository layout, added test-memory-system.sh documentation. All 39 tests pass (16 architecture + 12 memory + 11 bootstrap).
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# memory-lifecycle.sh — Manage project memory: recall, store, list, search
|
||||
#
|
||||
# Usage:
|
||||
# memory-lifecycle.sh recall <category> [query] — search memory for relevant entries
|
||||
# memory-lifecycle.sh store <category> <file> — add or update a memory entry
|
||||
# memory-lifecycle.sh list <category> — list entries in a category
|
||||
# memory-lifecycle.sh search <query> — full-text search across all memory
|
||||
# memory-lifecycle.sh sessions — list active/interrupted sessions
|
||||
# memory-lifecycle.sh cleanup — archive old completed sessions
|
||||
#
|
||||
# Categories: decisions, lessons, failures, architecture, sessions
|
||||
#
|
||||
# This script provides deterministic memory operations for the agent team.
|
||||
# It does NOT do semantic search — that is the Orchestrator's responsibility
|
||||
# using agent reasoning over the recalled entries.
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
MEMORY_DIR="$ROOT/memory"
|
||||
|
||||
usage() {
|
||||
sed -n '2,16p' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit 2
|
||||
}
|
||||
|
||||
# --- recall -----------------------------------------------------------------
|
||||
|
||||
cmd_recall() {
|
||||
local category="$1" query="${2:-}"
|
||||
local dir="$MEMORY_DIR/$category"
|
||||
|
||||
if [ ! -d "$dir" ]; then
|
||||
echo "ERROR: memory category '$category' does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Memory recall: $category ==="
|
||||
if [ -n "$query" ]; then
|
||||
echo "Query: $query"
|
||||
echo "---"
|
||||
# Search for matching entries (case-insensitive grep)
|
||||
local found=0
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
[ "$(basename "$f")" = "README.md" ] && continue
|
||||
if grep -qiF "$query" "$f" 2>/dev/null; then
|
||||
echo " FOUND: $(basename "$f")"
|
||||
# Show matching lines with context
|
||||
grep -iF "$query" "$f" | head -5 | sed 's/^/ /'
|
||||
found=$((found + 1))
|
||||
fi
|
||||
done
|
||||
if [ "$found" = "0" ]; then
|
||||
echo " No entries matching '$query' in $category"
|
||||
else
|
||||
echo " ---"
|
||||
echo " $found matching entr(y/ies)"
|
||||
fi
|
||||
else
|
||||
# List all entries
|
||||
local count=0
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
[ "$(basename "$f")" = "README.md" ] && continue
|
||||
echo " $(basename "$f")"
|
||||
count=$((count + 1))
|
||||
done
|
||||
if [ "$count" = "0" ]; then
|
||||
echo " No entries in $category"
|
||||
else
|
||||
echo " ---"
|
||||
echo " $count entr(y/ies)"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# --- store ------------------------------------------------------------------
|
||||
|
||||
cmd_store() {
|
||||
local category="$1" file="$2"
|
||||
local dir="$MEMORY_DIR/$category"
|
||||
|
||||
if [ ! -d "$dir" ]; then
|
||||
echo "ERROR: memory category '$category' does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "ERROR: source file '$file' does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local basename
|
||||
basename="$(basename "$file")"
|
||||
local dest="$dir/$basename"
|
||||
|
||||
if [ -f "$dest" ]; then
|
||||
echo "UPDATED: $category/$basename"
|
||||
else
|
||||
echo "CREATED: $category/$basename"
|
||||
fi
|
||||
|
||||
cp -p "$file" "$dest"
|
||||
}
|
||||
|
||||
# --- list -------------------------------------------------------------------
|
||||
|
||||
cmd_list() {
|
||||
local category="$1"
|
||||
cmd_recall "$category" ""
|
||||
}
|
||||
|
||||
# --- search -----------------------------------------------------------------
|
||||
|
||||
cmd_search() {
|
||||
local query="$1"
|
||||
echo "=== Full-text memory search: '$query' ==="
|
||||
local found=0
|
||||
|
||||
for category in decisions lessons failures architecture sessions; do
|
||||
local dir="$MEMORY_DIR/$category"
|
||||
[ -d "$dir" ] || continue
|
||||
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
[ "$(basename "$f")" = "README.md" ] && continue
|
||||
if grep -qiF "$query" "$f" 2>/dev/null; then
|
||||
echo " $category/$(basename "$f")"
|
||||
found=$((found + 1))
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [ "$found" = "0" ]; then
|
||||
echo " No entries matching '$query'"
|
||||
else
|
||||
echo " ---"
|
||||
echo " $found matching entr(y/ies) across all categories"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- sessions ---------------------------------------------------------------
|
||||
|
||||
cmd_sessions() {
|
||||
echo "=== Active/Interrupted Sessions ==="
|
||||
local dir="$MEMORY_DIR/sessions"
|
||||
local count=0
|
||||
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
[ "$(basename "$f")" = "README.md" ] && continue
|
||||
|
||||
local status
|
||||
status=$(grep -m1 "^Status:" "$f" 2>/dev/null | sed 's/^Status: *//' || echo "unknown")
|
||||
if [ "$status" = "active" ] || [ "$status" = "interrupted" ]; then
|
||||
local title
|
||||
title=$(grep -m1 "^# " "$f" 2>/dev/null | sed 's/^# //' || echo "$(basename "$f")")
|
||||
echo " [$status] $(basename "$f"): $title"
|
||||
count=$((count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$count" = "0" ]; then
|
||||
echo " No active or interrupted sessions"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- cleanup ----------------------------------------------------------------
|
||||
|
||||
cmd_cleanup() {
|
||||
echo "=== Session cleanup ==="
|
||||
local dir="$MEMORY_DIR/sessions"
|
||||
local archived=0
|
||||
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
[ "$(basename "$f")" = "README.md" ] && continue
|
||||
|
||||
local status
|
||||
status=$(grep -m1 "^Status:" "$f" 2>/dev/null | sed 's/^Status: *//' || echo "unknown")
|
||||
|
||||
if [ "$status" = "completed" ]; then
|
||||
# Check if older than 7 days
|
||||
local file_age
|
||||
file_age=$(( ($(date +%s) - $(stat -c %Y "$f" 2>/dev/null || echo 0)) / 86400 ))
|
||||
if [ "$file_age" -gt 7 ]; then
|
||||
local dest="$dir/archive"
|
||||
mkdir -p "$dest"
|
||||
mv "$f" "$dest/"
|
||||
echo " Archived: $(basename "$f") (age: ${file_age}d)"
|
||||
archived=$((archived + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$archived" = "0" ]; then
|
||||
echo " No sessions to archive"
|
||||
else
|
||||
echo " Archived $archived session(s)"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- main -------------------------------------------------------------------
|
||||
|
||||
[ $# -ge 1 ] || usage
|
||||
CMD="$1"; shift
|
||||
|
||||
case "$CMD" in
|
||||
recall)
|
||||
[ $# -ge 1 ] || { echo "ERROR: recall requires a category" >&2; usage; }
|
||||
cmd_recall "$1" "${2:-}"
|
||||
;;
|
||||
store)
|
||||
[ $# -ge 2 ] || { echo "ERROR: store requires a category and file" >&2; usage; }
|
||||
cmd_store "$1" "$2"
|
||||
;;
|
||||
list)
|
||||
[ $# -ge 1 ] || { echo "ERROR: list requires a category" >&2; usage; }
|
||||
cmd_list "$1"
|
||||
;;
|
||||
search)
|
||||
[ $# -ge 1 ] || { echo "ERROR: search requires a query" >&2; usage; }
|
||||
cmd_search "$1"
|
||||
;;
|
||||
sessions)
|
||||
cmd_sessions
|
||||
;;
|
||||
cleanup)
|
||||
cmd_cleanup
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown command: $CMD" >&2
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@@ -63,7 +63,7 @@ fi
|
||||
if assert_contains "$ORCH" "## Action Catalog (choose the next best action)" \
|
||||
&& assert_contains "$ORCH" "| # | Action | Purpose | Inputs | Outputs | Read-only | Cost | Risk | Prereq | Failure modes |" \
|
||||
&& assert_contains "$ORCH" "A1 | inspect repository" \
|
||||
&& assert_contains "$ORCH" "A24 | re-plan"; then
|
||||
&& grep -qE "A[0-9]+ . re-plan" "$ORCH"; then
|
||||
ok "T03 action catalog with tool cards present"
|
||||
else
|
||||
fail "T03 action catalog with tool cards present" "catalog/table markers missing"
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
|
||||
# test-memory-system.sh — Structural tests for the memory and skills systems
|
||||
#
|
||||
# Verifies:
|
||||
# 1. Memory directory structure exists and is well-formed
|
||||
# 2. Memory categories have READMEs with format templates
|
||||
# 3. Skills are properly structured with frontmatter
|
||||
# 4. Skill files have required sections
|
||||
# 5. Orchestrator references memory system
|
||||
# 6. Orchestrator references skills system
|
||||
# 7. Improvement proposal system structure exists
|
||||
# 8. Memory lifecycle script is executable and has correct usage
|
||||
# 9. Skills have owner metadata matching agent roster
|
||||
# 10. No skill duplicates agent core behavior
|
||||
#
|
||||
# Exit codes: 0 = all pass, 1 = any failure
|
||||
|
||||
TEAM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
AGENTS="$TEAM_ROOT/agents"
|
||||
MEMORY="$TEAM_ROOT/memory"
|
||||
SKILLS="$TEAM_ROOT/skills"
|
||||
IMPROVEMENTS="$TEAM_ROOT/improvements"
|
||||
|
||||
PASS=0; FAIL=0
|
||||
|
||||
ok() { PASS=$((PASS+1)); echo "PASS $1"; }
|
||||
fail(){ FAIL=$((FAIL+1)); echo "FAIL $1 — $2" >&2; }
|
||||
|
||||
assert_contains() { grep -qF "$2" "$1" 2>/dev/null && return 0; return 1; }
|
||||
assert_file() { [ -f "$1" ] && return 0; return 1; }
|
||||
assert_dir() { [ -d "$1" ] && return 0; return 1; }
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 1: Memory directory structure
|
||||
# ======================================================================== #
|
||||
if assert_dir "$MEMORY" && \
|
||||
assert_dir "$MEMORY/decisions" && \
|
||||
assert_dir "$MEMORY/lessons" && \
|
||||
assert_dir "$MEMORY/failures" && \
|
||||
assert_dir "$MEMORY/architecture" && \
|
||||
assert_dir "$MEMORY/sessions" && \
|
||||
assert_file "$MEMORY/MEMORY.md"; then
|
||||
ok "T01 memory directory structure exists with all categories"
|
||||
else
|
||||
fail "T01 memory directory structure exists with all categories" "missing directories or MEMORY.md"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 2: Memory categories have READMEs
|
||||
# ======================================================================== #
|
||||
README_OK=1
|
||||
for cat in decisions lessons failures architecture sessions; do
|
||||
assert_file "$MEMORY/$cat/README.md" || README_OK=0
|
||||
done
|
||||
if [ "$README_OK" = "1" ] && assert_file "$MEMORY/MEMORY.md"; then
|
||||
ok "T02 all memory categories have README.md files"
|
||||
else
|
||||
fail "T02 all memory categories have README.md files" "missing README in one or more categories"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 3: Memory READMEs contain format templates
|
||||
# ======================================================================== #
|
||||
FORMAT_OK=1
|
||||
assert_contains "$MEMORY/decisions/README.md" "## Decision" || FORMAT_OK=0
|
||||
assert_contains "$MEMORY/lessons/README.md" "## What was learned" || FORMAT_OK=0
|
||||
assert_contains "$MEMORY/failures/README.md" "## Root Cause" || FORMAT_OK=0
|
||||
assert_contains "$MEMORY/architecture/README.md" "## Ownership" || FORMAT_OK=0
|
||||
assert_contains "$MEMORY/sessions/README.md" "## Task" || FORMAT_OK=0
|
||||
if [ "$FORMAT_OK" = "1" ]; then
|
||||
ok "T03 memory READMEs contain format templates"
|
||||
else
|
||||
fail "T03 memory READMEs contain format templates" "missing format sections"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 4: Memory lifecycle script exists and is functional
|
||||
# ======================================================================== #
|
||||
HELP_OUTPUT=""
|
||||
HELP_OK=0
|
||||
if assert_file "$TEAM_ROOT/scripts/memory-lifecycle.sh"; then
|
||||
HELP_OUTPUT="$(bash "$TEAM_ROOT/scripts/memory-lifecycle.sh" --help 2>&1 || true)"
|
||||
echo "$HELP_OUTPUT" | grep -q "recall" && echo "$HELP_OUTPUT" | grep -q "store" && \
|
||||
echo "$HELP_OUTPUT" | grep -q "list" && echo "$HELP_OUTPUT" | grep -q "search" && HELP_OK=1
|
||||
fi
|
||||
if [ "$HELP_OK" = "1" ]; then
|
||||
ok "T04 memory lifecycle script exists with correct commands"
|
||||
else
|
||||
fail "T04 memory lifecycle script exists with correct commands" "script missing or help broken"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 5: Skills directory structure
|
||||
# ======================================================================== #
|
||||
if assert_dir "$SKILLS" && assert_file "$SKILLS/SKILLS.md"; then
|
||||
# Check that skill directories have SKILL.md files
|
||||
SKILL_COUNT=0
|
||||
for d in "$SKILLS"/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
[ "$(basename "$d")" = "SKILLS.md" ] 2>/dev/null && continue
|
||||
if assert_file "$d/SKILL.md"; then
|
||||
SKILL_COUNT=$((SKILL_COUNT + 1))
|
||||
fi
|
||||
done
|
||||
if [ "$SKILL_COUNT" -ge 5 ]; then
|
||||
ok "T05 skills directory has $SKILL_COUNT skills with SKILL.md files"
|
||||
else
|
||||
fail "T05 skills directory has SKILL.md files" "only $SKILL_COUNT skills found (expected ≥5)"
|
||||
fi
|
||||
else
|
||||
fail "T05 skills directory structure" "SKILLS.md or skills dir missing"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 6: Skill files have required frontmatter
|
||||
# ======================================================================== #
|
||||
FRONT_OK=1
|
||||
FRONT_COUNT=0
|
||||
for d in "$SKILLS"/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
[ -f "$d/SKILL.md" ] || continue
|
||||
FRONT_COUNT=$((FRONT_COUNT + 1))
|
||||
head -10 "$d/SKILL.md" | grep -q "^---$" || FRONT_OK=0
|
||||
head -10 "$d/SKILL.md" | grep -q "^name:" || FRONT_OK=0
|
||||
head -10 "$d/SKILL.md" | grep -q "^description:" || FRONT_OK=0
|
||||
head -10 "$d/SKILL.md" | grep -q "^version:" || FRONT_OK=0
|
||||
head -10 "$d/SKILL.md" | grep -q "^owner:" || FRONT_OK=0
|
||||
done
|
||||
if [ "$FRONT_OK" = "1" ] && [ "$FRONT_COUNT" -ge 5 ]; then
|
||||
ok "T06 all $FRONT_COUNT skill files have required frontmatter (name, description, version, owner)"
|
||||
else
|
||||
fail "T06 skill files have required frontmatter" "front_ok=$FRONT_OK count=$FRONT_COUNT"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 7: Skill files have required sections
|
||||
# ======================================================================== #
|
||||
SECTION_OK=1
|
||||
for d in "$SKILLS"/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
[ -f "$d/SKILL.md" ] || continue
|
||||
assert_contains "$d/SKILL.md" "## When to use" || SECTION_OK=0
|
||||
assert_contains "$d/SKILL.md" "## Core methodology" || \
|
||||
assert_contains "$d/SKILL.md" "## Step-by-step" || SECTION_OK=0
|
||||
done
|
||||
if [ "$SECTION_OK" = "1" ]; then
|
||||
ok "T07 skill files have 'When to use' and methodology sections"
|
||||
else
|
||||
fail "T07 skill files have required sections" "missing required sections"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 8: Orchestrator references memory system
|
||||
# ======================================================================== #
|
||||
ORCH="$AGENTS/orchestrator.md"
|
||||
ORCH_MEM_OK=0
|
||||
assert_contains "$ORCH" "memory" && \
|
||||
(grep -qE "recall|RECALL|project memory|memory.*lifecycle|memory.*before" "$ORCH" 2>/dev/null) && \
|
||||
ORCH_MEM_OK=1
|
||||
if [ "$ORCH_MEM_OK" = "1" ]; then
|
||||
ok "T08 orchestrator references memory system"
|
||||
else
|
||||
fail "T08 orchestrator references memory system" "no memory references in orchestrator"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 9: Orchestrator references skills system
|
||||
# ======================================================================== #
|
||||
ORCH_SKILL_OK=0
|
||||
assert_contains "$ORCH" "skill" && \
|
||||
(grep -qE "load.*skill|skill.*load|relevant skill|SKILL\.md|skills/" "$ORCH" 2>/dev/null) && \
|
||||
ORCH_SKILL_OK=1
|
||||
if [ "$ORCH_SKILL_OK" = "1" ]; then
|
||||
ok "T09 orchestrator references skills system"
|
||||
else
|
||||
fail "T09 orchestrator references skills system" "no skill loading references in orchestrator"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 10: Improvement proposal system exists
|
||||
# ======================================================================== #
|
||||
if assert_dir "$IMPROVEMENTS" && \
|
||||
assert_dir "$IMPROVEMENTS/pending" && \
|
||||
assert_dir "$IMPROVEMENTS/applied" && \
|
||||
assert_dir "$IMPROVEMENTS/rejected" && \
|
||||
assert_file "$IMPROVEMENTS/README.md" && \
|
||||
assert_contains "$IMPROVEMENTS/README.md" "human approval"; then
|
||||
ok "T10 improvement proposal system exists with approval requirement"
|
||||
else
|
||||
fail "T10 improvement proposal system exists" "missing directories or approval requirement"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 11: All subagents reference memory system
|
||||
# ======================================================================== #
|
||||
SUBAGENT_MEM_OK=1
|
||||
for sub in explorer detective builder reviewer maintainer writer tester toolsmith architect designer philosopher workflow-architect; do
|
||||
f="$AGENTS/$sub.md"
|
||||
if [ ! -f "$f" ]; then
|
||||
fail "T11 all subagents reference memory" "missing $sub.md"
|
||||
SUBAGENT_MEM_OK=0
|
||||
break
|
||||
fi
|
||||
if ! grep -q "Memory & Skills Awareness" "$f" 2>/dev/null; then
|
||||
fail "T11 all subagents reference memory" "$sub.md missing 'Memory & Skills Awareness'"
|
||||
SUBAGENT_MEM_OK=0
|
||||
break
|
||||
fi
|
||||
if ! grep -q "memory-lifecycle.sh" "$f" 2>/dev/null; then
|
||||
fail "T11 all subagents reference memory" "$sub.md missing memory-lifecycle.sh reference"
|
||||
SUBAGENT_MEM_OK=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$SUBAGENT_MEM_OK" = "1" ]; then
|
||||
ok "T11 all subagents reference memory system"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST 12: All subagents reference skills system
|
||||
# ======================================================================== #
|
||||
SUBAGENT_SKILL_OK=1
|
||||
for sub in explorer detective builder reviewer maintainer writer tester toolsmith architect designer philosopher workflow-architect; do
|
||||
f="$AGENTS/$sub.md"
|
||||
if ! grep -q "skill path\|SKILL\.md\|skills/" "$f" 2>/dev/null; then
|
||||
fail "T12 all subagents reference skills" "$sub.md missing skill references"
|
||||
SUBAGENT_SKILL_OK=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$SUBAGENT_SKILL_OK" = "1" ]; then
|
||||
ok "T12 all subagents reference skills system"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# Summary
|
||||
# ======================================================================== #
|
||||
echo
|
||||
echo "==================== SUMMARY ===================="
|
||||
echo "PASS: $PASS FAIL: $FAIL"
|
||||
[ "$FAIL" = "0" ] && echo "RESULT: ALL PASS" || echo "RESULT: FAILURES PRESENT"
|
||||
exit $(( FAIL > 0 ? 1 : 0 ))
|
||||
Reference in New Issue
Block a user