#!/bin/bash
# Report task status to the orchestrator
# Supports both CLI flags (preferred) and legacy JSON argument.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/../../_common/lib.sh"

print_usage() {
  cat <<'EOF_USAGE'
Usage:
  # CLI flags (preferred — avoids shell escaping issues)
  bash execute.sh --session dev-1 --status done --summary "Fixed the bug" --project /path/to/project \
    --artifact src/fix.ts --command "npm test" --exit-code 0 --output-tail "42 passed"

  # Could not finish: done with a blocked step is recorded as BLOCKED, not done
  bash execute.sh --session dev-1 --status done --summary "Fix written, tests blocked" \
    --blocked-step "npm test" --blocked-reason "test DB unreachable"

  # Summary from stdin (for multi-line or special characters)
  echo "Fixed the bug — it's working" | bash execute.sh --session dev-1 --status done --project /path

  # Summary from file
  bash execute.sh --session dev-1 --status done --summary-file /tmp/summary.txt --project /path

  # Legacy JSON argument (backward compatible)
  bash execute.sh '{"sessionName":"dev-1","status":"done","summary":"Fixed the bug"}'

Options:
  --session  | -s   Agent session name (required)
  --status   | -S   Status: done, blocked, failed, in_progress, active, milestone (required)
                       milestone = a SHIPPED artifact inside an open goal (PR merged,
                       spec finalized, build pass on a non-trivial change). Emits a
                       [MILESTONE] envelope that orc's Smart Notification Protocol
                       always forwards to the owner. See:
                       config/sops/common/mid-flight-milestone-surface.md (issue #435)
  --summary  | -m   Status summary text (required unless piped via stdin)
  --summary-file    Read summary from file path
  --project  | -p   Project path for auto-remember on completion
  --role     | -r   Agent role for the recorded learning (default: $CREWLY_ROLE, else "agent")
  --task-path       Task file path to auto-complete
  --task-id         Task ID (for structured StatusReport format)
  --progress        Progress percentage (0-100, for structured format)
  --structured      Use structured StatusReport format (true/false)
  --json     | -j   Raw JSON payload (same as legacy)

Evidence (status done — #873: "done" needs evidence):
  --artifact <path>        A file the work produced (absolute, relative to the
                           project/worktree, or an https URL). Must exist. Repeatable.
  --command <cmd>          A command you ran. Follow it with --exit-code (required)
  --exit-code <n>          and optionally --output-tail. Repeatable as a group.
  --output-tail <text>     Last lines of that command's output.
  --blocked-step <step>    You could not finish: the step that failed ...
  --blocked-reason <why>   ... and why. The WorkItem is recorded as BLOCKED, not done.
  --evidence <json>        Raw evidence array, e.g. '[{"type":"artifact","path":"/abs/f"}]'
  A non-zero --exit-code or a missing artifact is rejected by the server. Without
  any evidence, done is refused and the WorkItem stays open (#1312).
  --help     | -h   Show this help
EOF_USAGE
}

INPUT_JSON=""
SESSION_NAME=""
STATUS=""
SUMMARY=""
PROJECT_PATH=""
AGENT_ROLE=""
TASK_PATH=""
TASK_ID=""
WORK_ITEM_ID=""
PROGRESS=""
STRUCTURED="false"
# Evidence entries built from flags (#873), as a JSON array.
FLAG_EVIDENCE='[]'
BLOCKED_STEP=""
BLOCKED_REASON=""
EVIDENCE_ARG=""

# add_evidence <jq-object-expression> [jq --arg pairs...]
# Appends one entry to FLAG_EVIDENCE.
add_evidence() {
  local expr="$1"; shift
  FLAG_EVIDENCE=$(printf '%s' "$FLAG_EVIDENCE" | jq -c "$@" ". + [${expr}]")
}

# set_last_command_field <field> <jq value expression> [jq --arg/--argjson pairs...]
# Sets a field on the most recent --command entry; refuses when there is none.
set_last_command_field() {
  local field="$1" valexpr="$2" flag="$3"; shift 3
  if [ "$(printf '%s' "$FLAG_EVIDENCE" | jq -r 'if length > 0 and .[-1].type == "command" then "yes" else "no" end')" != "yes" ]; then
    error_exit "${flag} must follow --command <cmd>"
  fi
  FLAG_EVIDENCE=$(printf '%s' "$FLAG_EVIDENCE" | jq -c "$@" ".[-1].${field} = ${valexpr}")
}

# Detect legacy JSON argument as the first parameter
if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
  INPUT_JSON="$1"
  shift || true
fi

while [[ $# -gt 0 ]]; do
  case "$1" in
    --session|-s)
      SESSION_NAME="$2"
      shift 2
      ;;
    --status|-S)
      STATUS="$2"
      shift 2
      ;;
    --summary|-m)
      SUMMARY="$2"
      shift 2
      ;;
    --summary-file)
      SUMMARY="$(cat "$2")"
      shift 2
      ;;
    --project|-p)
      PROJECT_PATH="$2"
      shift 2
      ;;
    --role|-r)
      AGENT_ROLE="$2"
      shift 2
      ;;
    --task-path)
      TASK_PATH="$2"
      shift 2
      ;;
    --task-id)
      TASK_ID="$2"
      shift 2
      ;;
    --work-item-id|--wi-id)
      WORK_ITEM_ID="$2"
      shift 2
      ;;
    --progress)
      PROGRESS="$2"
      shift 2
      ;;
    --structured)
      STRUCTURED="true"
      shift
      ;;
    --json|-j)
      INPUT_JSON="$2"
      shift 2
      ;;
    --artifact)
      add_evidence '{type: "artifact", path: $p}' --arg p "$2"
      shift 2
      ;;
    --command)
      add_evidence '{type: "command", command: $c}' --arg c "$2"
      shift 2
      ;;
    --exit-code)
      [[ "$2" =~ ^-?[0-9]+$ ]] || error_exit "--exit-code must be an integer (got \"$2\")"
      set_last_command_field exitCode '($n | tonumber)' --exit-code --arg n "$2"
      shift 2
      ;;
    --output-tail)
      set_last_command_field outputTail '$t' --output-tail --arg t "$2"
      shift 2
      ;;
    --blocked-step)
      BLOCKED_STEP="$2"
      shift 2
      ;;
    --blocked-reason)
      BLOCKED_REASON="$2"
      shift 2
      ;;
    --evidence)
      EVIDENCE_ARG="$2"
      shift 2
      ;;
    --help|-h)
      print_usage
      exit 0
      ;;
    --)
      shift
      break
      ;;
    *)
      if [[ -z "$INPUT_JSON" && ${1:0:1} == '{' ]]; then
        INPUT_JSON="$1"
        shift
      else
        unknown_arg_exit "$1"
      fi
      ;;
  esac
done

# If nothing provided yet but stdin has data, read it as summary
if [ -z "$INPUT_JSON" ] && [ -z "$SUMMARY" ] && [ ! -t 0 ]; then
  STDIN_DATA="$(cat)"
  if [[ ${STDIN_DATA:0:1} == '{' ]]; then
    INPUT_JSON="$STDIN_DATA"
  else
    SUMMARY="$STDIN_DATA"
  fi
fi

# Parse JSON input if provided (backward compatible)
if [ -n "$INPUT_JSON" ]; then
  INPUT=$(read_json_input "$INPUT_JSON")
  [ -z "$SESSION_NAME" ] && SESSION_NAME=$(printf '%s' "$INPUT" | jq -r '.sessionName // empty')
  [ -z "$STATUS" ] && STATUS=$(printf '%s' "$INPUT" | jq -r '.status // empty')
  [ -z "$SUMMARY" ] && SUMMARY=$(printf '%s' "$INPUT" | jq -r '.summary // empty')
  [ -z "$TASK_PATH" ] && TASK_PATH=$(printf '%s' "$INPUT" | jq -r '.taskPath // empty')
  [ -z "$TASK_ID" ] && TASK_ID=$(printf '%s' "$INPUT" | jq -r '.taskId // empty')
  [ -z "$WORK_ITEM_ID" ] && WORK_ITEM_ID=$(printf '%s' "$INPUT" | jq -r '.workItemId // empty')
  [ -z "$PROGRESS" ] && PROGRESS=$(printf '%s' "$INPUT" | jq -r '.progress // empty')
  [ -z "$PROJECT_PATH" ] && PROJECT_PATH=$(printf '%s' "$INPUT" | jq -r '.projectPath // empty')
  [ -z "$AGENT_ROLE" ] && AGENT_ROLE=$(printf '%s' "$INPUT" | jq -r '.role // .agentRole // empty')
  ARTIFACTS=$(printf '%s' "$INPUT" | jq -c '.artifacts // empty')
  BLOCKERS=$(printf '%s' "$INPUT" | jq -c '.blockers // empty')
  [ -z "$EVIDENCE_ARG" ] && EVIDENCE_ARG=$(printf '%s' "$INPUT" | jq -c '.evidence // empty')
  USE_STRUCTURED=$(printf '%s' "$INPUT" | jq -r '.structured // "false"')
  [ "$USE_STRUCTURED" = "true" ] && STRUCTURED="true"
else
  ARTIFACTS=""
  BLOCKERS=""
fi

# Assemble the evidence block (#873): --evidence / JSON `evidence` first, then
# the entries from --artifact / --command / --blocked-step flags.
if [ -n "$EVIDENCE_ARG" ]; then
  if ! printf '%s' "$EVIDENCE_ARG" | jq -e 'type == "array"' >/dev/null 2>&1; then
    error_exit "evidence must be a JSON array, e.g. '[{\"type\":\"artifact\",\"path\":\"/abs/file\"}]'"
  fi
else
  EVIDENCE_ARG='[]'
fi
if [ -n "$BLOCKED_STEP" ] || [ -n "$BLOCKED_REASON" ]; then
  [ -n "$BLOCKED_STEP" ] && [ -n "$BLOCKED_REASON" ] || error_exit "--blocked-step and --blocked-reason go together: name the step that failed and why"
  add_evidence '{type: "blocked", step: $s, reason: $r}' --arg s "$BLOCKED_STEP" --arg r "$BLOCKED_REASON"
fi
if [ "$(printf '%s' "$FLAG_EVIDENCE" | jq '[.[] | select(.type == "command" and (has("exitCode") | not))] | length')" != "0" ]; then
  error_exit "every --command needs --exit-code <n> after it: the exit code is the evidence"
fi
EVIDENCE=$(jq -cn --argjson a "$EVIDENCE_ARG" --argjson b "$FLAG_EVIDENCE" '$a + $b')

require_param "sessionName (--session)" "$SESSION_NAME"
require_param "status (--status)" "$STATUS"
require_param "summary (--summary)" "$SUMMARY"

# The agent's role labels its learnings. Agent sessions carry it in
# CREWLY_ROLE (set by the backend at session creation); /memory/record-learning
# rejects an empty agentRole, so fall back to a generic label.
[ -z "$AGENT_ROLE" ] && AGENT_ROLE="${CREWLY_ROLE:-agent}"

# Gate: milestone summaries must carry a real "WHAT shipped + WHAT it means
# for the owner" — see config/sops/common/mid-flight-milestone-surface.md
# (issue #435). Reject anything that looks like "task done" boilerplate
# (under 30 chars) so the [MILESTONE] envelope doesn't get diluted.
if [ "$STATUS" = "milestone" ]; then
  SUMMARY_LEN=${#SUMMARY}
  if [ "$SUMMARY_LEN" -lt 30 ]; then
    echo "{\"error\":\"milestone gate failed: summary too short (${SUMMARY_LEN} chars, minimum 30). Per the Mid-Flight Milestone Surface SOP, a milestone surface must carry both WHAT shipped AND WHAT-IT-MEANS-FOR-OWNER.\"}" >&2
    error_exit "Summary must be at least 30 characters when reporting milestone (got ${SUMMARY_LEN})"
  fi
fi

# Gate: before_mark_done — reject empty or trivially short summaries
# This prevents agents from marking tasks done without meaningful output.
if [ "$STATUS" = "done" ]; then
  SUMMARY_LEN=${#SUMMARY}
  if [ "$SUMMARY_LEN" -lt 20 ]; then
    echo "{\"error\":\"before_mark_done gate failed: summary too short (${SUMMARY_LEN} chars, minimum 20). Provide a meaningful summary of what was accomplished.\"}" >&2
    error_exit "Summary must be at least 20 characters when reporting done (got ${SUMMARY_LEN})"
  fi
fi

# Map simple status strings to InProgressTaskStatus values
map_status_to_state() {
  case "$1" in
    done|completed) echo "completed" ;;
    blocked) echo "blocked" ;;
    failed) echo "failed" ;;
    in_progress|working) echo "working" ;;
    # Issue #435: `milestone` is a SHIPPED artifact inside an open goal,
    # not the outer goal's completion. It maps to its own state so the
    # downstream consumer (auto-learning subscriber, milestone-notification
    # subscriber #438, orc Smart Notification table row #436) can branch.
    milestone) echo "milestone" ;;
    *) echo "$1" ;;
  esac
}

# Build the message the orchestrator will receive
if [ "$STRUCTURED" = "true" ] && [ -n "$TASK_ID" ]; then
  # Structured StatusReport format for hierarchical teams
  STATE=$(map_status_to_state "$STATUS")
  MESSAGE="---\n[STATUS REPORT]\nTask ID: ${TASK_ID}\nState: ${STATE}"
  [ -n "$PROGRESS" ] && MESSAGE="${MESSAGE}\nProgress: ${PROGRESS}%"
  MESSAGE="${MESSAGE}\nReported by: ${SESSION_NAME}\n---\n\n## Status\n${SUMMARY}"

  # Add artifacts if provided
  if [ -n "$ARTIFACTS" ] && [ "$ARTIFACTS" != "" ]; then
    ARTIFACT_LINES=$(printf '%s' "$ARTIFACTS" | jq -r '.[]? | "- **\(.name)** (\(.type)): \(.content)"' 2>/dev/null || true)
    if [ -n "$ARTIFACT_LINES" ]; then
      MESSAGE="${MESSAGE}\n\n## Artifacts\n${ARTIFACT_LINES}"
    fi
  fi

  # Add blockers if provided
  if [ -n "$BLOCKERS" ] && [ "$BLOCKERS" != "" ]; then
    BLOCKER_LINES=$(printf '%s' "$BLOCKERS" | jq -r '.[]? // empty' 2>/dev/null | while read -r b; do printf '%s\n' "- ${b}"; done)
    if [ -n "$BLOCKER_LINES" ]; then
      MESSAGE="${MESSAGE}\n\n## Blockers\n${BLOCKER_LINES}"
    fi
  fi
else
  # Legacy format (backwards compatible)
  STATUS_UPPER=$(echo "$STATUS" | tr '[:lower:]' '[:upper:]')
  MESSAGE="[${STATUS_UPPER}] Agent ${SESSION_NAME}: ${SUMMARY}"
fi

# The message is built with "\n" inside double quotes (a literal backslash-n):
# send real newlines (2026-10-02 — the server's status detection and the
# orchestrator's reader both expect them).
_NL=$'\n'; MESSAGE="${MESSAGE//\\n/$_NL}"

# Send the status message to the orchestrator session via the chat API
# workItemId (when known) lets the server route the report to whoever owns
# that work — its team lead's review, or the orchestrator for work it delegated.
BODY=$(jq -n --arg content "$MESSAGE" --arg senderName "$SESSION_NAME" --arg workItemId "$WORK_ITEM_ID" \
  '{content: $content, senderName: $senderName, senderType: "agent"} + (if $workItemId != "" then {workItemId: $workItemId} else {} end)')

api_call POST "/chat/agent-response" "$BODY"

# Mark V3 WorkItem(s) for this session as complete when status=done.
# Per spec/2026-05-06-task-management-v1-deprecation.md, V3 task-pool is now
# the source of truth — we no longer move .md files via the v1
# `/task-management/complete{-by-session}` endpoints.
#
# Resolution order for which WorkItem to complete:
#   1. `workItemId` in input — explicit, always wins. PASS THIS.
#   2. Exactly one running WI for this session — inferred, and the resolved id
#      is echoed so the caller can see what was closed.
#   3. More than one running WI — REFUSED. The candidates are named and the
#      caller must pass `workItemId`.
#
# Why (3) refuses instead of guessing: this block used to complete
# `.data[0].id`, an arbitrary first running item. On 2026-08-21 that silently
# closed WorkItem 7db3b00c — a queued piece of work nobody had started — while
# the agent was reporting a DIFFERENT WI done. Nothing failed. The false
# completion then spawned a verify WorkItem asking the orchestrator to verify a
# delivery that had never happened, so one silent mis-close also consumed
# downstream verification attention on fiction.
#
# Inferring from a single running claim is safe and stays. Inferring from
# several is a coin flip on which real work gets destroyed.
#
# The legacy `taskPath` input is still accepted for backwards compatibility
# but no longer drives the API call.
if [ "$STATUS" = "done" ]; then
  TARGET_WI_ID="${WORK_ITEM_ID:-}"
  WI_RESOLUTION="explicit"

  if [ -z "$TARGET_WI_ID" ]; then
    POOL_RESP=$(api_call_full GET "/task-pool/items?status=running&target=${SESSION_NAME}" 2>/dev/null || echo '{}')
    RUNNING_IDS=$(printf '%s' "$POOL_RESP" | jq -r '((.workItems // .data // []) | map(.id)) | .[]' 2>/dev/null || true)
    RUNNING_COUNT=$(printf '%s' "$RUNNING_IDS" | grep -c . || true)

    if [ "$RUNNING_COUNT" -gt 1 ]; then
      CANDIDATES=$(printf '%s' "$RUNNING_IDS" | tr '\n' ' ')
      error_exit "report-status refuses to guess which WorkItem to complete: ${RUNNING_COUNT} are running for ${SESSION_NAME} (${CANDIDATES}). Pass workItemId explicitly. Completing an arbitrary one silently closes work nobody did — and a false completion cascades into a verify WorkItem for a delivery that never happened."
    fi

    TARGET_WI_ID=$(printf '%s' "$RUNNING_IDS" | head -1)
    WI_RESOLUTION="inferred"
  fi

  if [ -n "$TARGET_WI_ID" ]; then
    # Hygiene #4: emit canonical body shape `{agentId, result:{summary}}`
    # required by /api/task-pool/complete (task-pool.controller.ts
    # `completeItem`). Prior shape `{summary}` 400'd with
    # `agentId is required` + `summary required in body.result`,
    # forcing every worker into a direct-curl workaround.
    #
    # #873: the evidence block goes in result.evidence. Omitted when empty;
    # the server then refuses a deliverable's completion (#1312).
    COMPLETE_BODY=$(jq -n \
      --arg agentId "$SESSION_NAME" \
      --arg summary "$SUMMARY" \
      --argjson evidence "$EVIDENCE" \
      '{agentId: $agentId, result: ({summary: $summary} + (if ($evidence | length) > 0 then {evidence: $evidence} else {} end))}')
    # No `|| true`. A swallowed failure here is the inverse of the bug above:
    # report-status would announce success while the WorkItem stayed open, and
    # that is equally invisible. Report what actually happened either way.
    if COMPLETE_RESULT=$(api_call POST "/task-pool/complete/${TARGET_WI_ID}" "$COMPLETE_BODY" 2>&1); then
      # Echo the resolved id even on the happy path. Silent-but-correct is how
      # this class hides — the caller must be able to see WHICH item closed.
      # A `blocked` evidence entry is recorded as blocked, not done (#873);
      # a completion without evidence carries the server's warning.
      COMPLETE_WARNING=$(printf '%s' "$COMPLETE_RESULT" | jq -rs '(map(select(type == "object" and has("success"))) | last // {}) | .warning // empty' 2>/dev/null || true)
      COMPLETE_RECORDED=$(printf '%s' "$COMPLETE_RESULT" | jq -rs '(map(select(type == "object" and has("success"))) | last // {}) | .recordedAs // empty' 2>/dev/null || true)
      if [ "$COMPLETE_RECORDED" = "blocked" ]; then
        jq -n --arg id "$TARGET_WI_ID" --arg how "$WI_RESOLUTION" \
          '{workItem: $id, markedAs: "blocked", resolvedBy: $how, note: "blocked evidence: recorded as blocked, not done"}' >&2
      else
        jq -n --arg id "$TARGET_WI_ID" --arg how "$WI_RESOLUTION" --arg w "$COMPLETE_WARNING" \
          '{completedWorkItem: $id, resolvedBy: $how} + (if $w != "" then {warning: $w} else {} end)' >&2
      fi
    else
      NEXT_STEP=""
      case "$COMPLETE_RESULT" in
        *evidence_required*) NEXT_STEP="Run report-status --status done again with evidence: --artifact <file or https URL> (repeatable) and/or --command '<cmd>' --exit-code N; if you could not finish, --blocked-step '<step>' --blocked-reason '<why>'." ;;
      esac
      jq -n --arg id "$TARGET_WI_ID" --arg err "$COMPLETE_RESULT" --arg next "$NEXT_STEP" \
        '{warning: "status reported, but completing the WorkItem FAILED — it is still open", workItemId: $id, error: $err} + (if $next != "" then {next: $next} else {} end)' >&2
    fi
  fi
fi

# A blocked or failed report moves the WorkItem too (#842). It used to reach
# only the orchestrator's chat: the WorkItem stayed `running` with its claim
# held, the team lead got no task:blocked / task:failed event, and the reason
# lived only in a chat message. Same resolution rules as `done` above:
# explicit workItemId, else the single running item, else refuse.
if [ "$STATUS" = "blocked" ] || [ "$STATUS" = "failed" ]; then
  STOP_WI_ID="${WORK_ITEM_ID:-}"
  STOP_RESOLUTION="explicit"

  if [ -z "$STOP_WI_ID" ]; then
    POOL_RESP=$(api_call_full GET "/task-pool/items?status=running&target=${SESSION_NAME}" 2>/dev/null || echo '{}')
    RUNNING_IDS=$(printf '%s' "$POOL_RESP" | jq -r '((.workItems // .data // []) | map(.id)) | .[]' 2>/dev/null || true)
    RUNNING_COUNT=$(printf '%s' "$RUNNING_IDS" | grep -c . || true)

    if [ "$RUNNING_COUNT" -gt 1 ]; then
      CANDIDATES=$(printf '%s' "$RUNNING_IDS" | tr '\n' ' ')
      error_exit "Status reported, but report-status will not guess which WorkItem is ${STATUS}: ${RUNNING_COUNT} are running for ${SESSION_NAME} (${CANDIDATES}). Run it again with workItemId so the right item is marked ${STATUS} and its team lead is told."
    fi

    STOP_WI_ID=$(printf '%s' "$RUNNING_IDS" | head -1)
    STOP_RESOLUTION="inferred"
  fi

  if [ -n "$STOP_WI_ID" ]; then
    if [ "$STATUS" = "blocked" ]; then
      STOP_BODY=$(jq -n --arg agentId "$SESSION_NAME" --arg reason "$SUMMARY" '{agentId: $agentId, reason: $reason}')
      STOP_PATH="/task-pool/block/${STOP_WI_ID}"
    else
      STOP_BODY=$(jq -n --arg agentId "$SESSION_NAME" --arg error "$SUMMARY" '{agentId: $agentId, error: $error}')
      STOP_PATH="/task-pool/fail/${STOP_WI_ID}"
    fi
    if STOP_RESULT=$(api_call POST "$STOP_PATH" "$STOP_BODY" 2>&1); then
      jq -n --arg id "$STOP_WI_ID" --arg how "$STOP_RESOLUTION" --arg status "$STATUS" \
        '{workItem: $id, markedAs: $status, resolvedBy: $how}' >&2
    else
      jq -n --arg id "$STOP_WI_ID" --arg err "$STOP_RESULT" --arg status "$STATUS" \
        '{warning: ("status reported, but marking the WorkItem " + $status + " FAILED — it is still open"), workItemId: $id, error: $err}' >&2
    fi
  fi
fi

# record_task_learning <label> <summary>
#
# Records the report as a project learning via POST /memory/record-learning.
# The controller requires {agentId, agentRole, projectPath, learning}; this
# call used to send {agentId, content, type}, so every one 400'd and the
# `2>/dev/null || true` hid it (#816). A failure is still non-fatal (the
# status report itself already went out) but is now surfaced on stderr.
record_task_learning() {
  local label="$1" text="$2" body result
  body=$(jq -n \
    --arg agentId "$SESSION_NAME" \
    --arg agentRole "$AGENT_ROLE" \
    --arg projectPath "$PROJECT_PATH" \
    --arg learning "${label}: ${text}" \
    --arg relatedTask "${WORK_ITEM_ID:-$TASK_ID}" \
    '{agentId: $agentId, agentRole: $agentRole, projectPath: $projectPath, learning: $learning}
      + (if $relatedTask != "" then {relatedTask: $relatedTask} else {} end)')
  if ! result=$(api_call POST "/memory/record-learning" "$body" 2>&1 >/dev/null); then
    jq -n --arg err "$result" '{warning: "status reported, but recording the learning FAILED", error: $err}' >&2
  fi
}

# A finished task's summary is a task log, not knowledge: it is stored on the
# WorkItem and in task-history.json, and is NOT saved to memory as a project
# decision or learning (#833). Durable learnings go through `remember` /
# `record-learning` explicitly.

# Growth: extract growth areas on task completion (keyword-based, no LLM).
if [ "$STATUS" = "done" ] && [ -n "$SUMMARY" ] && [ -n "$PROJECT_PATH" ]; then
  # Extract growth areas from summary (keyword-based, no LLM)
  GROWTH_BODY=$(jq -n \
    --arg sessionName "$SESSION_NAME" \
    --arg text "$SUMMARY" \
    '{sessionName: $sessionName, text: $text}')
  api_call POST "/growth/extract-areas" "$GROWTH_BODY" 2>/dev/null || true
fi

# Growth: record failures for learning when task fails or is blocked.
if [ "$STATUS" = "failed" ] || [ "$STATUS" = "blocked" ]; then
  if [ -n "$SUMMARY" ] && [ -n "$PROJECT_PATH" ]; then
    record_task_learning "Task ${STATUS}" "$SUMMARY"
  fi
fi
