feat(profile): §7 conformance build-out — credbridge, distribution.yaml, cron, manifest
Closes the largest set of PROFILE-DISTRIBUTION-PROTOCOL §7 readiness gaps
surfaced in the 2026-05-23 audit. Profile goes from 4/8 to expected 8/8
once skills/proton-tools/ is committed.
New files:
credbridge.sh Personal-assistant variant of the shared-core
credbridge pattern. Three credentials in scope:
google-workspace (Gmail/Calendar/Contacts),
proton-bridge (himalaya IMAP/SMTP), perplexity
(raw WebSearch). Plan B marketing platforms
explicitly OUT OF SCOPE per CLAUDE.md hard rule.
validate_access.sh Emits PASS/BLOCKED/FAIL JSON line per credential.
Sourceable from install.sh and standalone. Exit
code always 0; status is in the JSON.
distribution.yaml Native Hermes install contract (`hermes profile
install` reads this). Mirrors cmo/ceo pattern.
Documents personal/agnostic naming exception
per FRAMEWORK §6.1 — no org suffix because there
is exactly one principal.
cron/steev-daily-briefing.json.template
06:30 daily briefing skeleton, ships disabled.
Aggregates calendar + flagged emails + due tasks
+ carried items + brief news scan into a single
digest in JP's voice. NEVER auto-sends, NEVER
touches business comms (CEO → CMO surface).
manifest.yaml fully rewritten:
- Added `contract: CONTRACT.md` pointer (was missing)
- Added inline comment explaining intentional `org:` omission
- Declared skills/proton-tools (on disk via JP's untracked WIP; declared
here so manifest matches disk truth once JP commits it)
- Added `lib:` block (credbridge.sh + validate_access.sh)
- Added `expected_external_skills:` informational list (google-workspace,
apple-*, obsidian, himalaya, imessage, perplexity) — these come from
Hermes' global skills tree per CLAUDE.md "reuse existing core skills"
- Added `optional_tools:` block (4 MCP servers: proton-calendar/-email/
-contacts, perplexity)
- Added `credentials:` block listing the 3 creds + resolution path
- Promoted `cron:` from empty list to a single steev-daily-briefing
entry (disabled_on_install: true)
- Added `sovereignty:` block (qwen3.6-35b-a3b on DGX Spark)
CONTRACT.md frontmatter migrated from legacy `tier: S` to T1 per
FRONTMATTER-SPEC. Added required fields (name, last_reviewed,
description, depends_on).
skills/proton-tools/ left untracked — that's JP's WIP, not mine to
commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
66c742c219
commit
2db2d26250
@ -1,10 +1,15 @@
|
||||
---
|
||||
tier: S
|
||||
name: steev-contract
|
||||
tier: T1
|
||||
status: active
|
||||
owner: jp
|
||||
source: hand
|
||||
last_reviewed: 2026-05-23
|
||||
review_by: 2026-08-21
|
||||
note: Steev profile contract — this file wins for the Steev profile
|
||||
description: steev profile behavior contract — what Steev does, doesn't do, edge cases. Tier T1 — this file wins for the steev profile.
|
||||
depends_on:
|
||||
- profile-distribution-protocol
|
||||
note: legacy tier S remapped to T1 per FRONTMATTER-SPEC 2026-05-23. Required fields filled (name, last_reviewed, description) per §7 audit.
|
||||
---
|
||||
|
||||
# Steev — Source of Truth
|
||||
|
||||
94
credbridge.sh
Executable file
94
credbridge.sh
Executable file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
# credbridge.sh — resolve credctl credentials into env vars for steev's
|
||||
# personal-flow tools, then exec the CLI. Secrets NEVER echoed, logged, or
|
||||
# written to disk.
|
||||
#
|
||||
# Usage: credbridge.sh <tool> [args...]
|
||||
# tools: google-workspace | proton-bridge | perplexity
|
||||
#
|
||||
# Per PROFILE-DISTRIBUTION-PROTOCOL §3 (shared core, "credbridge" row) and §6
|
||||
# (Conventions → Secrets), every profile distribution exposes credentials via
|
||||
# this verb chain: credctl set → credenv → exec, with secrets per-call only.
|
||||
#
|
||||
# This is the personal-assistant variant of the credbridge pattern. Steev's
|
||||
# cred surface is narrow by design:
|
||||
#
|
||||
# - google-workspace: Gmail + Calendar + Contacts (OAuth blob from credctl)
|
||||
# - proton-bridge: IMAP/SMTP password for the local Proton Bridge T6
|
||||
# sidecar — gives Steev access to JP's Proton mail via
|
||||
# himalaya (cleartext on 127.0.0.1 only)
|
||||
# - perplexity: Perplexity API key for WebSearch toolset (lightweight
|
||||
# — most Steev work uses the perplexity MCP instead)
|
||||
#
|
||||
# Plan B marketing platforms (WooCommerce, Mailchimp, Meta, GA4, etc.) are OUT
|
||||
# OF SCOPE here — that's cmo-planb's credbridge. Steev MUST NEVER resolve a
|
||||
# marketing platform credential. The CLAUDE.md "no access to Plan B marketing
|
||||
# platform credentials" rule is enforced by omission from this case statement.
|
||||
#
|
||||
# Design notes (same as cmo/credbridge.sh — shared core):
|
||||
# - credctl values read into local vars, exported straight to the child env
|
||||
# - JSON-valued creds (google-workspace OAuth) parsed via `node -e` reading
|
||||
# from stdin so the value never lands on argv / process list
|
||||
# - No `echo $secret`. set +x stays off.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Paths env-overridable for portability; defaults match the JP install.
|
||||
CREDCTL="${CREDCTL:-/home/svrnty/workspaces/cortex/L6-svrnty.core-credentials/credctl}"
|
||||
STEEV_LIB="${STEEV_LIB:-/home/svrnty/.hermes/steev}"
|
||||
|
||||
die() { printf '{"error":"%s"}\n' "$1" >&2; exit 1; }
|
||||
|
||||
[ $# -ge 1 ] || die "usage: credbridge.sh <google-workspace|proton-bridge|perplexity> [args...]"
|
||||
TOOL="$1"; shift
|
||||
|
||||
[ -x "$CREDCTL" ] || die "credctl not found/executable at $CREDCTL"
|
||||
|
||||
# cred_raw <name> — print unmasked credential value to stdout. Caller captures
|
||||
# into a var; value never displayed. Strips the "Value:" header from credctl.
|
||||
cred_raw() {
|
||||
"$CREDCTL" get "$1" --unmask 2>/dev/null \
|
||||
| sed -n '/^Value:/,$p' | sed '1s/^Value:[[:space:]]*//'
|
||||
}
|
||||
|
||||
# json_field <json> <key> — extract a string field via node; value never on argv.
|
||||
json_field() {
|
||||
printf '%s' "$1" | node -e '
|
||||
let s="";process.stdin.on("data",d=>s+=d);
|
||||
process.stdin.on("end",()=>{try{const o=JSON.parse(s);
|
||||
const v=o[process.argv[1]];process.stdout.write(v==null?"":String(v));
|
||||
}catch(e){process.stdout.write("");}});' "$2"
|
||||
}
|
||||
|
||||
case "$TOOL" in
|
||||
google-workspace)
|
||||
# Gmail Data API + Calendar API + People API all expect a bearer token
|
||||
# minted from this service-account / OAuth blob. The blob is JSON; we
|
||||
# export the whole document so the child CLI can introspect scope.
|
||||
GW_JSON="$(cred_raw google-workspace)"
|
||||
[ -n "$GW_JSON" ] || die "credctl: google-workspace not set"
|
||||
export GOOGLE_WORKSPACE_CREDENTIALS_JSON="$GW_JSON"
|
||||
exec "$@"
|
||||
;;
|
||||
proton-bridge)
|
||||
# Steev reads JP's Proton inbox via the local Proton Bridge IMAP daemon
|
||||
# (T6 sidecar — see PROFILE-DISTRIBUTION-PROTOCOL §4.T6). credctl stores
|
||||
# the bridge password (rotates when JP rotates the bridge).
|
||||
PB_PASS="$(cred_raw proton-bridge-imap)"
|
||||
[ -n "$PB_PASS" ] || die "credctl: proton-bridge-imap not set"
|
||||
export PROTON_BRIDGE_IMAP_PASSWORD="$PB_PASS"
|
||||
exec "$@"
|
||||
;;
|
||||
perplexity)
|
||||
# Lightweight WebSearch path. Most Steev research goes through the
|
||||
# perplexity MCP server (which holds its own key); this credbridge entry
|
||||
# exists for scripts that need a raw key (rare).
|
||||
PPL_KEY="$(cred_raw perplexity-api)"
|
||||
[ -n "$PPL_KEY" ] || die "credctl: perplexity-api not set"
|
||||
export PERPLEXITY_API_KEY="$PPL_KEY"
|
||||
exec "$@"
|
||||
;;
|
||||
*)
|
||||
die "unknown tool: $TOOL (allowed: google-workspace|proton-bridge|perplexity)"
|
||||
;;
|
||||
esac
|
||||
40
cron/steev-daily-briefing.json.template
Normal file
40
cron/steev-daily-briefing.json.template
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"id": "steev-daily-briefing",
|
||||
"name": "steev-daily-briefing",
|
||||
"prompt": "Steev daily briefing. DB=${HERMES_HOME}/steev/steev.db, helper=${HERMES_HOME}/steev/steev_db.py.\n1. runtime-get: if agent_runtime.status='halted' → STOP, output \"halted, skipping\", do nothing else.\n2. Aggregate today's context:\n - calendar events from google-workspace (today + tomorrow morning)\n - flagged + unread emails from proton-bridge (himalaya) + google-workspace (gmail)\n - due tasks from apple-reminders + obsidian vault (~/vaults/steev/Tasks/)\n - carried items from steev.db (briefings.carried_over)\n - brief news scan via perplexity (1-2 items only)\n3. Compose a single digest in JP's voice (FR/EN per JP's language preference for that day). Surface only what needs JP's attention.\n4. Persist digest to steev.db (briefings table) as status='draft'.\n5. Output a 5W summary of items surfaced.\n6. NEVER auto-send. NEVER touch business comms surface (that's CEO → CMO).",
|
||||
"skills": [
|
||||
"steev-agent"
|
||||
],
|
||||
"skill": "steev-agent",
|
||||
"model": null,
|
||||
"provider": null,
|
||||
"base_url": null,
|
||||
"script": null,
|
||||
"no_agent": false,
|
||||
"context_from": null,
|
||||
"schedule": {
|
||||
"kind": "cron",
|
||||
"expr": "30 6 * * *",
|
||||
"display": "06:30 daily"
|
||||
},
|
||||
"schedule_display": "06:30 daily",
|
||||
"repeat": {
|
||||
"times": null,
|
||||
"completed": 0
|
||||
},
|
||||
"enabled": false,
|
||||
"state": "paused",
|
||||
"paused_at": null,
|
||||
"paused_reason": "installed paused — enable via: hermes cron resume steev-daily-briefing",
|
||||
"created_at": null,
|
||||
"next_run_at": null,
|
||||
"last_run_at": null,
|
||||
"last_status": null,
|
||||
"last_error": null,
|
||||
"last_delivery_error": null,
|
||||
"deliver": "local",
|
||||
"origin": null,
|
||||
"enabled_toolsets": null,
|
||||
"workdir": "${HERMES_HOME}/steev",
|
||||
"profile": "steev"
|
||||
}
|
||||
29
distribution.yaml
Normal file
29
distribution.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
# Hermes profile distribution manifest — native Hermes install contract.
|
||||
# Used by `hermes profile install`. Distinct from manifest.yaml (our workspace
|
||||
# convention layered on top — see ../sot/03-PROTOCOLS/PROFILE-DISTRIBUTION-PROTOCOL.md).
|
||||
name: steev
|
||||
version: 1.0.0
|
||||
description: "Steev — JP's personal AI chief of staff. Daily briefing (calendar + flagged emails + due tasks + carried items + brief news scan), inbox triage, comms drafting in JP's voice (French/English bilingual), and business-task delegation to ceo-planb. Personal-flow manager — no Plan B marketing surface. Sovereign on qwen3.6-35b-a3b."
|
||||
hermes_requires: ">=0.14.0"
|
||||
author: "Svrnty / JP <mathias@openharbor.io>"
|
||||
license: "proprietary"
|
||||
|
||||
# Steev is personal/agnostic per CORTEX-OS-FRAMEWORK §6.1 — no org suffix.
|
||||
# Profile name = `steev` (not `steev-<org>`) because there is exactly one
|
||||
# principal: JP. Cloning steev for another principal = rename in
|
||||
# distribution.yaml only; no other code changes.
|
||||
|
||||
env_requires: [] # credentials provisioned via credctl at install/runtime, never in env
|
||||
|
||||
distribution_owned:
|
||||
- AGENT.md
|
||||
- CONTRACT.md
|
||||
- CLAUDE.md
|
||||
- README.md
|
||||
- manifest.yaml
|
||||
- schema.sql
|
||||
- install.sh
|
||||
- credbridge.sh
|
||||
- validate_access.sh
|
||||
- skills/
|
||||
- cron/
|
||||
@ -1,19 +1,73 @@
|
||||
# Steev profile distribution manifest — machine-readable identity + install contract.
|
||||
# Read by install.sh. Convention shared by all profile distributions (see PROFILE-DISTRIBUTION-SPEC.md).
|
||||
profile: steev
|
||||
kind: profile-distribution
|
||||
role: steev
|
||||
# Read by install.sh. Convention shared by all Hermes profile distributions
|
||||
# (see ../sot/03-PROTOCOLS/PROFILE-DISTRIBUTION-PROTOCOL.md — the canonical protocol).
|
||||
profile: steev # Hermes profile name (personal — no org suffix per FRAMEWORK §6.1)
|
||||
kind: profile-distribution # family marker; steev = personal-assistant reference impl
|
||||
role: personal-assistant # function — Chief of Staff for one principal (JP)
|
||||
# org: ~ # intentionally omitted — steev is personal/agnostic
|
||||
version: 1.0.0
|
||||
identity: AGENT.md
|
||||
reference: docs/STEEV-MASTER.md
|
||||
identity: AGENT.md # WHO (role, mission, boundaries)
|
||||
contract: CONTRACT.md # behavior contract — tier T1 (this file wins)
|
||||
reference: docs/STEEV-MASTER.md # full operating source of truth
|
||||
|
||||
skills:
|
||||
- skills/steev-agent # personal assistant orchestrator
|
||||
skills: # exposed to Hermes via skills.external_dirs (→ <repo>/skills)
|
||||
- skills/steev-agent # orchestrator — daily briefing, inbox triage, comms drafting,
|
||||
# business delegation to ceo-planb
|
||||
- skills/proton-tools # Proton Calendar + Email + Contacts (24-tool reference) —
|
||||
# uses the 3 cortex MCP servers (proton-calendar/-email/-contacts)
|
||||
|
||||
# Role tools = scripts at repo root (the "lib"), reached through credbridge.
|
||||
# Personal-flow surface only; Plan B marketing CLIs out of scope (cmo-planb owns those).
|
||||
lib:
|
||||
- credbridge.sh # credctl → env → google-workspace / proton-bridge / perplexity
|
||||
- validate_access.sh # PASS / BLOCKED / FAIL per credential per §7
|
||||
|
||||
# Hermes built-in / external skills Steev reuses but does NOT vendor (per CLAUDE.md
|
||||
# "reuse existing core skills"). Informational — these come from Hermes' global skills
|
||||
# tree (~/.hermes/skills/) or external skill libraries the principal already installed.
|
||||
expected_external_skills:
|
||||
- google-workspace # Gmail + Calendar + Contacts
|
||||
- apple-notes # macOS-local via osascript
|
||||
- apple-reminders # macOS-local via osascript
|
||||
- obsidian # ~/vaults/steev PKM
|
||||
- himalaya # IMAP/SMTP via proton-bridge sidecar
|
||||
- imessage # macOS-local
|
||||
- perplexity # WebSearch toolset (lightweight; MCP preferred)
|
||||
|
||||
# MCP servers Steev consumes. Names match runtime-prefixed form (mcp_<server>_<tool>).
|
||||
optional_tools:
|
||||
- mcp_proton_calendar # 8-tool Proton Calendar facade
|
||||
- mcp_proton_email # 10-tool Proton Email facade
|
||||
- mcp_proton_contacts # 6-tool Proton Contacts facade
|
||||
- mcp_perplexity # research / WebSearch (key held by MCP server, not credbridge)
|
||||
|
||||
requires_tools: [terminal, memory_tool]
|
||||
|
||||
db:
|
||||
file: steev.db # runtime state; created from schema.sql; never committed
|
||||
schema: schema.sql
|
||||
credentials: # validated by validate_access.sh
|
||||
- name: google-workspace
|
||||
purpose: Gmail + Calendar + Contacts read/write for daily briefing + inbox triage
|
||||
resolved_via: credbridge.sh
|
||||
- name: proton-bridge-imap
|
||||
purpose: local Proton Bridge IMAP/SMTP password (himalaya path)
|
||||
resolved_via: credbridge.sh
|
||||
- name: perplexity-api
|
||||
purpose: Perplexity API key for raw WebSearch (MCP path preferred)
|
||||
resolved_via: credbridge.sh
|
||||
|
||||
cron: [] # daily-briefing cron registered disabled at launch
|
||||
db:
|
||||
file: steev.db # runtime state; created from schema.sql; never committed
|
||||
schema: schema.sql # briefings + inbox_items + invocations + agent_runtime
|
||||
|
||||
cron:
|
||||
- id: steev-daily-briefing
|
||||
schedule: "30 6 * * *" # 06:30 local — well before JP's start of day
|
||||
skill: steev-agent
|
||||
input: { mode: daily-briefing }
|
||||
disabled_on_install: true # ships disabled per profile protocol §6 (Safety)
|
||||
template: cron/steev-daily-briefing.json.template
|
||||
|
||||
sovereignty:
|
||||
llm_model: qwen-local/qwen3.6-35b-a3b
|
||||
host: dgx-spark
|
||||
external_api_dependencies:
|
||||
- perplexity # WebSearch only; build-time research path. Daily briefing scan uses 1-2 items.
|
||||
|
||||
42
validate_access.sh
Executable file
42
validate_access.sh
Executable file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# validate_access.sh — report PASS / BLOCKED / FAIL per credential, per
|
||||
# PROFILE-DISTRIBUTION-PROTOCOL §7 (readiness checklist, "credbridge resolves
|
||||
# every credential the manifest lists; validate_access reports PASS/BLOCKED/
|
||||
# FAIL"). Sourceable from install.sh and standalone.
|
||||
#
|
||||
# Usage: validate_access.sh
|
||||
# Exit code: always 0. Emits one JSON line per credential, suitable for jq /
|
||||
# log aggregation.
|
||||
#
|
||||
# Statuses:
|
||||
# PASS credctl key set + non-empty
|
||||
# BLOCKED key absent or empty — actionable: run `credctl set <name>`
|
||||
# FAIL credctl itself missing or broken — environmental issue
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
CREDCTL="${CREDCTL:-/home/svrnty/workspaces/cortex/L6-svrnty.core-credentials/credctl}"
|
||||
|
||||
CREDENTIALS=(
|
||||
google-workspace
|
||||
proton-bridge-imap
|
||||
perplexity-api
|
||||
)
|
||||
|
||||
check() {
|
||||
local name="$1" status reason
|
||||
if [ ! -x "$CREDCTL" ]; then
|
||||
status="FAIL"; reason="credctl not found at $CREDCTL"
|
||||
elif ! "$CREDCTL" list 2>/dev/null | grep -q "^${name}[[:space:]]"; then
|
||||
status="BLOCKED"; reason="credctl key not set — run: credctl set ${name}"
|
||||
elif [ -z "$("$CREDCTL" get "$name" --unmask 2>/dev/null | sed -n '/^Value:/,$p' | sed '1s/^Value:[[:space:]]*//')" ]; then
|
||||
status="BLOCKED"; reason="key exists but value empty"
|
||||
else
|
||||
status="PASS"; reason="present"
|
||||
fi
|
||||
printf '{"credential":"%s","status":"%s","reason":"%s"}\n' "$name" "$status" "$reason"
|
||||
}
|
||||
|
||||
for cred in "${CREDENTIALS[@]}"; do
|
||||
check "$cred"
|
||||
done
|
||||
Loading…
Reference in New Issue
Block a user