Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions .opencode/command/otherness.setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,18 +230,43 @@ else
fi
```

## Step 8 — Deploy otherness scheduled workflow

The otherness scheduled workflow enables the autonomous loop to run automatically without
human intervention. This step deploys it to `.github/workflows/` from the otherness repo.

```bash
OTHERNESS_WORKFLOW="$HOME/.otherness/.github/workflows/otherness-scheduled.yml"
TARGET_DIR=".github/workflows"
TARGET_FILE="$TARGET_DIR/otherness-scheduled.yml"

if [ -f "$TARGET_FILE" ]; then
echo "otherness-scheduled.yml already present — skipping."
elif [ ! -f "$OTHERNESS_WORKFLOW" ]; then
echo "⚠️ ~/.otherness/.github/workflows/otherness-scheduled.yml not found."
echo " Ensure ~/.otherness is cloned from pnz1990/otherness and run this setup again."
else
mkdir -p "$TARGET_DIR"
cp "$OTHERNESS_WORKFLOW" "$TARGET_FILE"
echo "Deployed otherness-scheduled.yml to $TARGET_FILE"
# Stage and commit the workflow file
git add "$TARGET_FILE" 2>/dev/null || true
echo "Workflow file staged. Commit and push to activate the scheduled loop."
fi
```

## Done

Edit `otherness-config.yaml` to set your `BUILD_COMMAND`, `TEST_COMMAND`, `LINT_COMMAND`, and other project-specific values. If your project has a UI, add `project.job_family: FEE`; for platform/infrastructure projects use `SysDE`; backend-only projects can omit the field (defaults to `SDE`).

**Before running `/otherness.run`**: edit `docs/aide/vision.md` to describe your project. This is the most important thing — the autonomous team reads it on every startup.

**To activate the scheduled loop (optional but recommended):**
The loop can run automatically every 6 hours via GitHub Actions — no human needed.
**To activate the scheduled loop:**
The workflow was deployed to `.github/workflows/otherness-scheduled.yml` in Step 8.
1. Go to: GitHub repo → Settings → Secrets and variables → Actions → New repository secret
2. Add `ANTHROPIC_API_KEY` (or your LLM provider key) as a secret name
3. In `otherness-config.yaml`, uncomment the `schedule:` section and set your cron
4. `.github/workflows/otherness-scheduled.yml` is already present and will fire on schedule
3. Commit and push the workflow file (if not already done)
4. The loop will fire automatically on schedule

See `docs/design/19-scheduled-execution.md` for full details.

Expand Down
233 changes: 232 additions & 1 deletion .opencode/command/otherness.status.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,238 @@ FLEET_MODE=false
echo "$ARGS" | grep -q "\-\-fleet" && FLEET_MODE=true
```

## Step 1 — Read state (single-project mode)
## Step 0 — Health dashboard (single-project mode, design doc 35 §Future → ✅)

Skip if `FLEET_MODE=true`. Prints a compact one-page health view readable in 30 seconds.

```bash
if [ "$FLEET_MODE" != "true" ]; then

REPO=$(git remote get-url origin 2>/dev/null | sed 's|.*github.com[:/]||;s|\.git$||')

git fetch origin _state --quiet 2>/dev/null || true
git show origin/_state:.otherness/state.json > .otherness/state.json 2>/dev/null || true

python3 - << 'DASHBOARD_EOF'
import json, re, os, subprocess, datetime, tempfile

REPO = os.environ.get('REPO', '')

print("=" * 60)
print("otherness STATUS")
print("=" * 60)

# ── 3-line quick glance (design doc 06 §Future → ✅) ─────────
# A human should answer (1) healthy? (2) last shipped? (3) moving? in one glance
try:
ql_health = "UNKNOWN"
ql_last = "(none)"
ql_queue = "?"
try:
metrics_content = open('docs/aide/metrics.md').read()
rows = []
for line in metrics_content.splitlines():
if '|' not in line: continue
cells = [c.strip() for c in line.split('|')[1:-1]]
if len(cells) >= 10 and cells[0].startswith('20'):
rows.append(cells[9] if len(cells) > 9 else '?')
if rows:
recent = rows[-3:]
feat = sum(1 for o in recent if o in ('feature-rich', 'mixed'))
ql_health = "ADVANCING" if feat >= 2 else "STALLING"
except Exception: pass
try:
import subprocess as _sp
r = _sp.run(['gh','pr','list','--repo',REPO,'--state','merged','--limit','5',
'--json','title,mergedAt',
'--jq','[.[] | select(.title | test("^feat|^fix|^refactor";"i"))][0].title'],
capture_output=True, text=True, timeout=10)
if r.returncode == 0 and r.stdout.strip() and r.stdout.strip() != 'null':
ql_last = r.stdout.strip().strip('"')[:50]
except Exception: pass
try:
with open('.otherness/state.json') as f: _s = json.load(f)
ql_queue = str(len([d for d in _s.get('features',{}).values() if d.get('state')=='todo']))
except Exception: pass
print(f"Health: {ql_health} | Last: {ql_last} | Queue: {ql_queue} todo")
print("-" * 60)
except Exception: pass

# ── Section 1: Health signal with trend ─────────────────────
print()
print("1. HEALTH SIGNAL")
health = "UNKNOWN"
trend = "UNKNOWN"
try:
content = open('docs/aide/metrics.md').read()
rows = []
for line in content.splitlines():
if '|' not in line: continue
cells = [c.strip() for c in line.split('|')[1:-1]]
if len(cells) >= 11 and cells[0].startswith('20'):
outcome = cells[9] if len(cells) > 9 else ''
rows.append({'date': cells[0], 'outcome': outcome})
if rows:
last = rows[-1]
recent = rows[-5:]
feature_count = sum(1 for r in recent if r.get('outcome') in ('feature-rich', 'mixed'))
if feature_count >= 3:
trend = "ADVANCING"
elif feature_count == 2:
trend = "STEADY"
else:
trend = "STALLING"
outcomes = [r.get('outcome','?') for r in recent]
print(f" Trend (last {len(recent)} batches): {trend}")
print(f" Outcomes: {' | '.join(outcomes)}")
else:
print(" Metrics: no batch rows yet")
trend = "UNKNOWN"
except Exception as e:
print(f" Metrics: unavailable ({e})")

# ── Section 2: Skills ────────────────────────────────────────
print()
print("2. SKILLS")
try:
skills_dir = os.path.expanduser('~/.otherness/agents/skills')
skill_files = [f for f in os.listdir(skills_dir)
if f.endswith('.md') and f not in ('PROVENANCE.md', 'README.md')]
print(f" Count: {len(skill_files)} skills")
try:
provenance = open(os.path.join(skills_dir, 'PROVENANCE.md')).read()
dates = re.findall(r'^## (\d{4}-\d{2}-\d{2})', provenance, re.MULTILINE)
if dates:
last_learn = datetime.date.fromisoformat(sorted(dates)[-1])
days_since = (datetime.date.today() - last_learn).days
flag = " ⚠️ OVERDUE" if days_since > 14 else ""
print(f" Last learn: {last_learn} ({days_since}d ago){flag}")
else:
print(" Last learn: never (PROVENANCE.md empty)")
except:
print(" Last learn: unavailable")
except Exception as e:
print(f" Skills: unavailable ({e})")

# ── Section 3: Queue ─────────────────────────────────────────
print()
print("3. QUEUE")
try:
with open('.otherness/state.json') as f: s = json.load(f)
features = s.get('features', {})
todo = [(k, v) for k, v in features.items() if v.get('state') == 'todo']
in_flight = [(k, v) for k, v in features.items()
if v.get('state') in ('assigned', 'in_review')]
PRIORITY_MAP = {'critical': 0, 'high': 1, 'medium': 2, 'low': 3}
todo.sort(key=lambda x: (PRIORITY_MAP.get(x[1].get('priority'), 4), x[0]))
print(f" Todo: {len(todo)} items | In-flight: {len(in_flight)}")
if todo:
next_item = todo[0]
print(f" Next: {next_item[0]} [{next_item[1].get('priority','?')}] {next_item[1].get('title','?')[:55]}")
else:
print(" Next: (queue empty — COORD will generate)")
except Exception as e:
print(f" Queue: unavailable ({e})")

# ── Section 4: Journey status ────────────────────────────────
print()
print("4. JOURNEYS")
try:
dod = open('docs/aide/definition-of-done.md').read()
journeys = re.findall(r'^## (Journey \d+[^\n]*)', dod, re.MULTILINE)
done_count = len(re.findall(r'✅', dod))
pending_count = len(re.findall(r'🔲', dod))
print(f" {len(journeys)} journeys | {done_count} ✅ done | {pending_count} 🔲 pending")
for j in journeys[:5]:
print(f" - {j[:55]}")
if len(journeys) > 5:
print(f" ... +{len(journeys)-5} more")
except Exception as e:
print(f" Journeys: unavailable ({e})")

# ── Section 5: Simulation ────────────────────────────────────
print()
print("5. SIMULATION")
state_wt = os.path.join(tempfile.gettempdir(), 'otherness-status-' + str(os.getpid()))
sim_shown = False
try:
if os.path.exists(state_wt):
subprocess.run(['git','worktree','remove',state_wt,'--force'], capture_output=True)
subprocess.run(['git','worktree','add','--no-checkout',state_wt,'origin/_state'],
capture_output=True, check=True)
pred_file = os.path.join(state_wt, '.otherness', 'sim-prediction.json')
subprocess.run(['git','-C',state_wt,'checkout','_state','--','.otherness/sim-prediction.json'],
capture_output=True)
if os.path.exists(pred_file):
pred = json.load(open(pred_file))
calib_at = pred.get('calibrated_at', '?')[:10]
try:
calib_date = datetime.date.fromisoformat(calib_at)
calib_age = (datetime.date.today() - calib_date).days
calib_flag = " ⚠️ STALE" if calib_age > 14 else ""
except:
calib_age = '?'
calib_flag = ''
arch_conv = float(pred.get('arch_convergence_score', 0))
arch_flag = " ⚠️ AMBER" if arch_conv >= 0.7 else ""
source = pred.get('source', '?')
print(f" Calibrated: {calib_at} ({calib_age}d ago){calib_flag}")
print(f" Source: {source}")
print(f" arch_convergence: {arch_conv:.3f}{arch_flag}")
print(f" Next batch floor: {pred.get('prs_next_batch_floor','?')} — ceiling: {pred.get('prs_next_batch_ceiling','?')}")
sim_shown = True
else:
print(" sim-prediction.json not found on _state branch")
except Exception as e:
print(f" Simulation: unavailable ({e})")
finally:
try:
subprocess.run(['git','worktree','remove',state_wt,'--force'], capture_output=True)
except: pass
subprocess.run(['git','worktree','prune'], capture_output=True)

# ── Section 6: Reference project health ─────────────────────
print()
print("6. REFERENCE PROJECT")
try:
reference = None
in_monitor = in_projects = False
for line in open('otherness-config.yaml'):
if re.match(r'^monitor:', line): in_monitor = True
if in_monitor and re.match(r'\s+projects:', line): in_projects = True
if in_projects:
m = re.match(r'\s+- (.+)', line)
if m:
r = m.group(1).strip()
if not r.endswith('/otherness'):
reference = r
break
if reference:
r = subprocess.run(['gh','api',f'repos/{reference}/branches/_state',
'--jq','.commit.commit.committer.date'],
capture_output=True, text=True, timeout=10)
if r.returncode == 0 and r.stdout.strip():
ts = datetime.datetime.fromisoformat(r.stdout.strip().replace('Z','+00:00'))
hours = (datetime.datetime.now(datetime.timezone.utc) - ts).total_seconds() / 3600
flag = " ⚠️ STALE" if hours > 72 else ""
print(f" {reference}: last activity {hours:.1f}h ago{flag}")
else:
print(f" {reference}: no _state branch (not yet running?)")
else:
print(" No non-otherness project in monitor.projects")
except Exception as e:
print(f" Reference project: unavailable ({e})")

# ── Summary line ─────────────────────────────────────────────
print()
print(f"Health: {trend} | Run /otherness.run to advance.")
print("=" * 60)
DASHBOARD_EOF

fi
```



Skip if `FLEET_MODE=true`.

Expand Down
Loading
Loading