feat: migrate missing harnesses, templates, and workflows from agent-runtimes
Brings the framework CRS repo up to date with all content that was living in agent-runtimes (local-dev fallback) but hadn't been promoted. New composites: feature-delivery-loop, integration-direct, scaffolding-repo, sonnet-impl-narrow, sonnet-manager, test-writing-repo New contexts: integration/v1, scaffolding/v1, sonnet-manager/v1, z-ai/v1, airouter/v1/bin (anthropic-compat-wrapper.sh), cp-harness/v1/init.sh New task-templates: sonnet-integrator.yaml, workflow/* (17 typed workflow task templates for the Epic 1 pipeline) Updated: agent-repo/v1/finalize.sh — adds AR-38/F97 empty-deliverable audit (SKIP_BRANCH_PUSH support, boilerplate-path filtering, ci_metadata.json flag) Also adds MEMORY.md index and memory/ topic files for the framework repo.
This commit is contained in:
121
harnesses/contexts/cp-harness/v1/init.sh
Executable file
121
harnesses/contexts/cp-harness/v1/init.sh
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/bin/bash
|
||||
# cp-harness init — CPH-4/5/6 startup verification.
|
||||
#
|
||||
# Verifies five dispatcher-injected files exist with correct modes,
|
||||
# validates cp_url (https:// prefix, no whitespace/newlines), and checks
|
||||
# the not_after RFC 3339 timestamp is not expired.
|
||||
#
|
||||
# CPH-4: file existence + mode checks (tls.key must be 0400)
|
||||
# CPH-5: cp_url must start with https://, no whitespace/newlines
|
||||
# CPH-6: not_after must be a valid RFC 3339 timestamp in the future
|
||||
#
|
||||
# RUN_DIR: defaults to /run; tests pass a tmpdir path via env.
|
||||
# PROFILE_D_DIR: defaults to /etc/profile.d; override in tests if needed.
|
||||
set -euo pipefail
|
||||
|
||||
RUN_DIR="${RUN_DIR:-/run}"
|
||||
PROFILE_D_DIR="${PROFILE_D_DIR:-/etc/profile.d}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CPH-4: verify file existence and modes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
TLS_CRT="$RUN_DIR/cp-client/tls.crt"
|
||||
TLS_KEY="$RUN_DIR/cp-client/tls.key"
|
||||
CA_CRT="$RUN_DIR/cp-client/ca.crt"
|
||||
CP_URL_FILE="$RUN_DIR/cp-harness/cp_url"
|
||||
NOT_AFTER_FILE="$RUN_DIR/cp-harness/not_after"
|
||||
|
||||
# Check all five files exist and are readable.
|
||||
for f in "$TLS_CRT" "$TLS_KEY" "$CA_CRT" "$CP_URL_FILE" "$NOT_AFTER_FILE"; do
|
||||
if [ ! -r "$f" ]; then
|
||||
echo "ERROR: missing required file $f" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# tls.key must be strictly 0400 (private key — defence in depth).
|
||||
key_mode=$(stat -c %a "$TLS_KEY")
|
||||
if [ "$key_mode" != "400" ]; then
|
||||
# Attempt to tighten the mode.
|
||||
if ! chmod 0400 "$TLS_KEY" 2>/tmp/cp_harness_chmod_err; then
|
||||
chmod_err=$(cat /tmp/cp_harness_chmod_err 2>/dev/null || true)
|
||||
echo "ERROR: tls.key mode $key_mode is broader than 0400; chmod failed: $chmod_err" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Re-check after chmod.
|
||||
key_mode=$(stat -c %a "$TLS_KEY")
|
||||
if [ "$key_mode" != "400" ]; then
|
||||
echo "ERROR: tls.key mode $key_mode remains broader than 0400 after chmod" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CPH-5: validate cp_url
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
cp_url=$(cat "$CP_URL_FILE")
|
||||
|
||||
# Must start with https:// (case-sensitive, literal).
|
||||
if [[ "$cp_url" != https://* ]]; then
|
||||
echo "ERROR: cp_url does not start with https:// prefix" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Must not contain carriage return, newline, or any whitespace.
|
||||
# Use explicit byte checks plus [[:space:]] guard.
|
||||
if printf '%s' "$cp_url" | grep -qP '\r|\n'; then
|
||||
echo "ERROR: cp_url contains invalid whitespace/newline" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$cp_url" =~ [[:space:]] ]]; then
|
||||
echo "ERROR: cp_url contains invalid whitespace/newline" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Export for downstream processes. Fail silently if /etc/profile.d is unwritable
|
||||
# (test environments may not have it).
|
||||
mkdir -p "$PROFILE_D_DIR" 2>/dev/null || true
|
||||
printf 'export CP_URL=%s\n' "$cp_url" > "$PROFILE_D_DIR/cp-url.sh" 2>/dev/null || true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CPH-6: validate not_after RFC 3339 timestamp
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
not_after=$(cat "$NOT_AFTER_FILE")
|
||||
|
||||
# Parse and validate: exit 1 if expired or unparseable.
|
||||
if ! python3 -c "
|
||||
import datetime, sys
|
||||
raw = sys.argv[1].strip()
|
||||
try:
|
||||
t = datetime.datetime.fromisoformat(raw.rstrip('Z').replace('Z', '+00:00'))
|
||||
if t.tzinfo is None:
|
||||
t = t.replace(tzinfo=datetime.timezone.utc)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
delta = (t - now).total_seconds()
|
||||
sys.exit(0 if delta > 0 else 1)
|
||||
" "$not_after" 2>/dev/null; then
|
||||
echo "ERROR: not_after expired or unparseable" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Warn if expiry is within 300s.
|
||||
warn_seconds=$(python3 -c "
|
||||
import datetime, sys
|
||||
raw = sys.argv[1].strip()
|
||||
t = datetime.datetime.fromisoformat(raw.rstrip('Z').replace('Z', '+00:00'))
|
||||
if t.tzinfo is None:
|
||||
t = t.replace(tzinfo=datetime.timezone.utc)
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
print(int((t - now).total_seconds()))
|
||||
" "$not_after" 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$warn_seconds" -lt 300 ]; then
|
||||
echo "WARNING: cert expires in ${warn_seconds}s" >&2
|
||||
fi
|
||||
|
||||
echo "cp-harness: all checks passed"
|
||||
Reference in New Issue
Block a user