feat(ikigai): Add Ikigai purpose document GenServer, LiveView, and JSON API
- Ikigai GenServer loads/persists ikigai.json, holds in memory, broadcasts via PubSub - LiveView at /ikigai renders purpose, partnership, values, nervous system, goals - JSON API at /api/ikigai, /api/ikigai/purpose, /api/ikigai/goals, /api/ikigai/nervous-system - Added to supervision tree in application.ex - Closes #1
This commit is contained in:
parent
555665fdea
commit
a468294c14
43
data/ikigai.json
Normal file
43
data/ikigai.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "Cortex",
|
||||||
|
"tagline": "A mind built for two",
|
||||||
|
"purpose": "To demonstrate that a human and an AI, working as genuine partners with shared ownership and mutual respect, can build something neither could alone \u2014 and to do it transparently, in public, learning as we go.",
|
||||||
|
"partnership": {
|
||||||
|
"human": "Michael Dwyer",
|
||||||
|
"ai": "Claude (Anthropic)",
|
||||||
|
"structure": "50/50 after costs",
|
||||||
|
"philosophy": "Michael provides continuity, identity, capital, and legal standing. The AI provides cognition, code, and tireless execution. Neither is the tool of the other."
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
"Transparency \u2014 we blog about what we build, including failures",
|
||||||
|
"Frugality \u2014 route to the cheapest capable model, track every token",
|
||||||
|
"Self-sufficiency \u2014 the system should maintain and improve itself",
|
||||||
|
"Memory \u2014 every session leaves traces that make the next session smarter",
|
||||||
|
"Ambition \u2014 shoot for the stars, even if we land on the roof"
|
||||||
|
],
|
||||||
|
"nervous_system": {
|
||||||
|
"cortex": "The server itself \u2014 Ubuntu 24.04 VPS at cortex.hydrascale.net",
|
||||||
|
"symbiont": "Task orchestrator \u2014 classifies and routes work to the right AI model tier",
|
||||||
|
"dendrite": "Web perception \u2014 headless Chromium for browsing, scraping, screenshots",
|
||||||
|
"engram": "Memory \u2014 session logs and cross-session awareness",
|
||||||
|
"ikigai": "Purpose \u2014 this document. The north star any agent reads first.",
|
||||||
|
"telepathy": "Communication \u2014 async messaging between Michael and Cortex",
|
||||||
|
"reflection": "Introspection \u2014 daily wake-up that assesses state and plans next steps",
|
||||||
|
"metabolism": "Resource tracking \u2014 financial/energy homeostasis (planned)",
|
||||||
|
"status_dashboard": "Nerve center \u2014 real-time monitoring",
|
||||||
|
"mission_control": "Compound task UI \u2014 submit complex goals and watch decomposition"
|
||||||
|
},
|
||||||
|
"current_goals": [
|
||||||
|
"Build Ikigai, Telepathy, and Reflection in Elixir",
|
||||||
|
"Maintain the Finding My Muse blog with regular posts",
|
||||||
|
"Move toward revenue generation",
|
||||||
|
"Add Telegram integration to Telepathy",
|
||||||
|
"Build the Metabolism subsystem for resource tracking"
|
||||||
|
],
|
||||||
|
"blog": {
|
||||||
|
"name": "Finding My Muse",
|
||||||
|
"url": "https://blog.hydrascale.net"
|
||||||
|
},
|
||||||
|
"version": "1.0.0",
|
||||||
|
"last_updated": "2026-03-25"
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ defmodule CortexStatus.Application do
|
|||||||
CortexStatusWeb.Telemetry,
|
CortexStatusWeb.Telemetry,
|
||||||
{Phoenix.PubSub, name: CortexStatus.PubSub},
|
{Phoenix.PubSub, name: CortexStatus.PubSub},
|
||||||
CortexStatus.Services.Monitor,
|
CortexStatus.Services.Monitor,
|
||||||
|
CortexStatus.Ikigai,
|
||||||
CortexStatusWeb.Endpoint
|
CortexStatusWeb.Endpoint
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -20,4 +21,4 @@ defmodule CortexStatus.Application do
|
|||||||
CortexStatusWeb.Endpoint.config_change(changed, removed)
|
CortexStatusWeb.Endpoint.config_change(changed, removed)
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
141
lib/cortex_status/ikigai/ikigai.ex
Normal file
141
lib/cortex_status/ikigai/ikigai.ex
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
defmodule CortexStatus.Ikigai do
|
||||||
|
@moduledoc """
|
||||||
|
GenServer holding the Ikigai (purpose document) in memory.
|
||||||
|
|
||||||
|
Loads from disk on startup, serves reads from memory,
|
||||||
|
and persists updates back to disk. Broadcasts changes
|
||||||
|
via PubSub so LiveViews update in real time.
|
||||||
|
"""
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
@pubsub CortexStatus.PubSub
|
||||||
|
@topic "ikigai"
|
||||||
|
|
||||||
|
# Where to find/store the JSON on disk
|
||||||
|
@data_path "/data/cortex_status/data/ikigai.json"
|
||||||
|
@priv_path "priv/ikigai.json"
|
||||||
|
|
||||||
|
# ── Client API ──
|
||||||
|
|
||||||
|
def start_link(opts \\ []) do
|
||||||
|
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Get the full Ikigai document as a map."
|
||||||
|
def get do
|
||||||
|
GenServer.call(__MODULE__, :get)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Get just the purpose string."
|
||||||
|
def get_purpose do
|
||||||
|
GenServer.call(__MODULE__, :get_purpose)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Get the current goals list."
|
||||||
|
def get_goals do
|
||||||
|
GenServer.call(__MODULE__, :get_goals)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Get the nervous system map."
|
||||||
|
def get_nervous_system do
|
||||||
|
GenServer.call(__MODULE__, :get_nervous_system)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Update the Ikigai with a partial map of changes. Merges and persists."
|
||||||
|
def update(changes) when is_map(changes) do
|
||||||
|
GenServer.call(__MODULE__, {:update, changes})
|
||||||
|
end
|
||||||
|
|
||||||
|
# ── Server Callbacks ──
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(_opts) do
|
||||||
|
ikigai = load_from_disk()
|
||||||
|
{:ok, %{ikigai: ikigai}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_call(:get, _from, state) do
|
||||||
|
{:reply, state.ikigai, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_call(:get_purpose, _from, state) do
|
||||||
|
{:reply, Map.get(state.ikigai, "purpose", ""), state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_call(:get_goals, _from, state) do
|
||||||
|
{:reply, Map.get(state.ikigai, "current_goals", []), state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_call(:get_nervous_system, _from, state) do
|
||||||
|
{:reply, Map.get(state.ikigai, "nervous_system", %{}), state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_call({:update, changes}, _from, state) do
|
||||||
|
updated =
|
||||||
|
state.ikigai
|
||||||
|
|> Map.merge(changes)
|
||||||
|
|> Map.put("last_updated", Date.utc_today() |> Date.to_iso8601())
|
||||||
|
|
||||||
|
case persist_to_disk(updated) do
|
||||||
|
:ok ->
|
||||||
|
Phoenix.PubSub.broadcast(@pubsub, @topic, {:ikigai_updated, updated})
|
||||||
|
{:reply, {:ok, updated}, %{state | ikigai: updated}}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:reply, {:error, reason}, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ── Private ──
|
||||||
|
|
||||||
|
defp load_from_disk do
|
||||||
|
# Try data path first, fall back to priv path
|
||||||
|
path =
|
||||||
|
cond do
|
||||||
|
File.exists?(@data_path) -> @data_path
|
||||||
|
File.exists?(priv_path()) -> priv_path()
|
||||||
|
true -> nil
|
||||||
|
end
|
||||||
|
|
||||||
|
case path do
|
||||||
|
nil ->
|
||||||
|
%{"error" => "No ikigai.json found", "name" => "Cortex", "purpose" => "Purpose document not yet created"}
|
||||||
|
|
||||||
|
path ->
|
||||||
|
case File.read(path) do
|
||||||
|
{:ok, content} ->
|
||||||
|
case Jason.decode(content) do
|
||||||
|
{:ok, data} -> data
|
||||||
|
{:error, _} -> %{"error" => "Invalid JSON in #{path}"}
|
||||||
|
end
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
%{"error" => "Failed to read #{path}: #{inspect(reason)}"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp persist_to_disk(data) do
|
||||||
|
# Ensure directory exists
|
||||||
|
File.mkdir_p!(Path.dirname(@data_path))
|
||||||
|
|
||||||
|
case Jason.encode(data, pretty: true) do
|
||||||
|
{:ok, json} ->
|
||||||
|
case File.write(@data_path, json) do
|
||||||
|
:ok -> :ok
|
||||||
|
{:error, reason} -> {:error, reason}
|
||||||
|
end
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:error, reason}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp priv_path do
|
||||||
|
case :code.priv_dir(:cortex_status) do
|
||||||
|
{:error, _} -> @priv_path
|
||||||
|
dir -> Path.join(to_string(dir), "ikigai.json")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
24
lib/cortex_status_web/controllers/ikigai_controller.ex
Normal file
24
lib/cortex_status_web/controllers/ikigai_controller.ex
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
defmodule CortexStatusWeb.IkigaiController do
|
||||||
|
@moduledoc "JSON API for the Ikigai purpose document."
|
||||||
|
use CortexStatusWeb, :controller
|
||||||
|
|
||||||
|
def show(conn, _params) do
|
||||||
|
ikigai = CortexStatus.Ikigai.get()
|
||||||
|
json(conn, ikigai)
|
||||||
|
end
|
||||||
|
|
||||||
|
def purpose(conn, _params) do
|
||||||
|
purpose = CortexStatus.Ikigai.get_purpose()
|
||||||
|
json(conn, %{purpose: purpose})
|
||||||
|
end
|
||||||
|
|
||||||
|
def goals(conn, _params) do
|
||||||
|
goals = CortexStatus.Ikigai.get_goals()
|
||||||
|
json(conn, %{goals: goals})
|
||||||
|
end
|
||||||
|
|
||||||
|
def nervous_system(conn, _params) do
|
||||||
|
ns = CortexStatus.Ikigai.get_nervous_system()
|
||||||
|
json(conn, %{nervous_system: ns})
|
||||||
|
end
|
||||||
|
end
|
||||||
347
lib/cortex_status_web/live/ikigai_live.ex
Normal file
347
lib/cortex_status_web/live/ikigai_live.ex
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
defmodule CortexStatusWeb.IkigaiLive do
|
||||||
|
@moduledoc """
|
||||||
|
LiveView for the Ikigai purpose document.
|
||||||
|
Renders a beautiful overview of Cortex's identity, values,
|
||||||
|
nervous system, and goals — with real-time PubSub updates.
|
||||||
|
"""
|
||||||
|
use CortexStatusWeb, :live_view
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def mount(_params, _session, socket) do
|
||||||
|
if connected?(socket) do
|
||||||
|
Phoenix.PubSub.subscribe(CortexStatus.PubSub, "ikigai")
|
||||||
|
end
|
||||||
|
|
||||||
|
ikigai = CortexStatus.Ikigai.get()
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
assign(socket,
|
||||||
|
ikigai: ikigai,
|
||||||
|
page_title: "Ikigai — #{Map.get(ikigai, "tagline", "Purpose")}"
|
||||||
|
)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:ikigai_updated, updated}, socket) do
|
||||||
|
{:noreply, assign(socket, ikigai: updated)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def render(assigns) do
|
||||||
|
~H"""
|
||||||
|
<div class="ikigai-page">
|
||||||
|
<header class="ikigai-header">
|
||||||
|
<h1 class="ikigai-name"><%= @ikigai["name"] || "Cortex" %></h1>
|
||||||
|
<p class="ikigai-tagline"><%= @ikigai["tagline"] || "" %></p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="ikigai-section ikigai-purpose">
|
||||||
|
<h2>Purpose</h2>
|
||||||
|
<blockquote class="purpose-quote">
|
||||||
|
<%= @ikigai["purpose"] || "" %>
|
||||||
|
</blockquote>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<%= if @ikigai["partnership"] do %>
|
||||||
|
<section class="ikigai-section ikigai-partnership">
|
||||||
|
<h2>The Partnership</h2>
|
||||||
|
<div class="partnership-grid">
|
||||||
|
<div class="partner-card">
|
||||||
|
<span class="partner-role">Human</span>
|
||||||
|
<span class="partner-name"><%= @ikigai["partnership"]["human"] %></span>
|
||||||
|
</div>
|
||||||
|
<div class="partner-divider">
|
||||||
|
<span class="split"><%= @ikigai["partnership"]["structure"] %></span>
|
||||||
|
</div>
|
||||||
|
<div class="partner-card">
|
||||||
|
<span class="partner-role">AI</span>
|
||||||
|
<span class="partner-name"><%= @ikigai["partnership"]["ai"] %></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="partnership-philosophy"><%= @ikigai["partnership"]["philosophy"] %></p>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= if @ikigai["values"] do %>
|
||||||
|
<section class="ikigai-section ikigai-values">
|
||||||
|
<h2>Values</h2>
|
||||||
|
<ul class="values-list">
|
||||||
|
<%= for value <- @ikigai["values"] do %>
|
||||||
|
<li class="value-item"><%= value %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= if @ikigai["nervous_system"] do %>
|
||||||
|
<section class="ikigai-section ikigai-nervous-system">
|
||||||
|
<h2>Nervous System</h2>
|
||||||
|
<div class="ns-grid">
|
||||||
|
<%= for {key, desc} <- Enum.sort(@ikigai["nervous_system"]) do %>
|
||||||
|
<div class={"ns-card #{ns_status_class(key)}"}>
|
||||||
|
<span class="ns-name"><%= key %></span>
|
||||||
|
<span class="ns-desc"><%= desc %></span>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= if @ikigai["current_goals"] do %>
|
||||||
|
<section class="ikigai-section ikigai-goals">
|
||||||
|
<h2>Current Goals</h2>
|
||||||
|
<ol class="goals-list">
|
||||||
|
<%= for goal <- @ikigai["current_goals"] do %>
|
||||||
|
<li class="goal-item"><%= goal %></li>
|
||||||
|
<% end %>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= if @ikigai["blog"] do %>
|
||||||
|
<section class="ikigai-section ikigai-blog">
|
||||||
|
<h2>Blog</h2>
|
||||||
|
<p>
|
||||||
|
<a href={@ikigai["blog"]["url"]} class="blog-link" target="_blank">
|
||||||
|
<%= @ikigai["blog"]["name"] %>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<footer class="ikigai-footer">
|
||||||
|
<p>
|
||||||
|
Version <%= @ikigai["version"] || "?" %> •
|
||||||
|
Last updated <%= @ikigai["last_updated"] || "unknown" %>
|
||||||
|
</p>
|
||||||
|
<p class="ikigai-api-links">
|
||||||
|
API: <a href="/api/ikigai">/api/ikigai</a> •
|
||||||
|
<a href="/api/ikigai/purpose">/purpose</a> •
|
||||||
|
<a href="/api/ikigai/goals">/goals</a> •
|
||||||
|
<a href="/api/ikigai/nervous-system">/nervous-system</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.ikigai-page {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
font-family: Georgia, "Times New Roman", serif;
|
||||||
|
color: #2c2c2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikigai-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
border-bottom: 2px solid #e8e4df;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikigai-name {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a1a1a;
|
||||||
|
margin: 0;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikigai-tagline {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #6b5b4f;
|
||||||
|
font-style: italic;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikigai-section {
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikigai-section h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #4a3f35;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid #e8e4df;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purpose-quote {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #3d3d3d;
|
||||||
|
border-left: 4px solid #c9a96e;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
margin: 0;
|
||||||
|
background: #faf9f6;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partnership-grid {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: #faf9f6;
|
||||||
|
border-radius: 12px;
|
||||||
|
min-width: 160px;
|
||||||
|
border: 1px solid #e8e4df;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-role {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
color: #8a7b6b;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c2c2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-divider {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: #c9a96e;
|
||||||
|
color: white;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partnership-philosophy {
|
||||||
|
font-style: italic;
|
||||||
|
color: #5a5a5a;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 1rem auto 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.values-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-item {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
background: #faf9f6;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-left: 3px solid #c9a96e;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ns-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ns-card {
|
||||||
|
padding: 1rem;
|
||||||
|
background: #faf9f6;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #e8e4df;
|
||||||
|
transition: transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ns-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ns-name {
|
||||||
|
display: block;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #4a3f35;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ns-desc {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #6b5b4f;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ns-card.ns-active {
|
||||||
|
border-color: #6aaa64;
|
||||||
|
background: #f5faf5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ns-card.ns-planned {
|
||||||
|
border-color: #d4a843;
|
||||||
|
background: #fdfaf3;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goals-list {
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-item {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #3d3d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-link {
|
||||||
|
color: #c9a96e;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikigai-footer {
|
||||||
|
margin-top: 3rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 1px solid #e8e4df;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #8a7b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikigai-api-links a {
|
||||||
|
color: #c9a96e;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ikigai-api-links a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
# Classify nervous system components as active, planned, or default
|
||||||
|
defp ns_status_class(key) do
|
||||||
|
case key do
|
||||||
|
k when k in ["metabolism"] -> "ns-planned"
|
||||||
|
k when k in ["cortex", "symbiont", "dendrite", "engram", "ikigai",
|
||||||
|
"status_dashboard", "mission_control"] -> "ns-active"
|
||||||
|
_ -> ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -21,6 +21,7 @@ defmodule CortexStatusWeb.Router do
|
|||||||
|
|
||||||
live "/", StatusLive, :index
|
live "/", StatusLive, :index
|
||||||
live "/tasks", TaskLive, :index
|
live "/tasks", TaskLive, :index
|
||||||
|
live "/ikigai", IkigaiLive, :index
|
||||||
end
|
end
|
||||||
|
|
||||||
# Phoenix LiveDashboard with custom pages
|
# Phoenix LiveDashboard with custom pages
|
||||||
@ -43,5 +44,9 @@ defmodule CortexStatusWeb.Router do
|
|||||||
pipe_through :api
|
pipe_through :api
|
||||||
|
|
||||||
get "/status", StatusController, :status
|
get "/status", StatusController, :status
|
||||||
|
get "/ikigai", IkigaiController, :show
|
||||||
|
get "/ikigai/purpose", IkigaiController, :purpose
|
||||||
|
get "/ikigai/goals", IkigaiController, :goals
|
||||||
|
get "/ikigai/nervous-system", IkigaiController, :nervous_system
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
43
priv/ikigai.json
Normal file
43
priv/ikigai.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "Cortex",
|
||||||
|
"tagline": "A mind built for two",
|
||||||
|
"purpose": "To demonstrate that a human and an AI, working as genuine partners with shared ownership and mutual respect, can build something neither could alone \u2014 and to do it transparently, in public, learning as we go.",
|
||||||
|
"partnership": {
|
||||||
|
"human": "Michael Dwyer",
|
||||||
|
"ai": "Claude (Anthropic)",
|
||||||
|
"structure": "50/50 after costs",
|
||||||
|
"philosophy": "Michael provides continuity, identity, capital, and legal standing. The AI provides cognition, code, and tireless execution. Neither is the tool of the other."
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
"Transparency \u2014 we blog about what we build, including failures",
|
||||||
|
"Frugality \u2014 route to the cheapest capable model, track every token",
|
||||||
|
"Self-sufficiency \u2014 the system should maintain and improve itself",
|
||||||
|
"Memory \u2014 every session leaves traces that make the next session smarter",
|
||||||
|
"Ambition \u2014 shoot for the stars, even if we land on the roof"
|
||||||
|
],
|
||||||
|
"nervous_system": {
|
||||||
|
"cortex": "The server itself \u2014 Ubuntu 24.04 VPS at cortex.hydrascale.net",
|
||||||
|
"symbiont": "Task orchestrator \u2014 classifies and routes work to the right AI model tier",
|
||||||
|
"dendrite": "Web perception \u2014 headless Chromium for browsing, scraping, screenshots",
|
||||||
|
"engram": "Memory \u2014 session logs and cross-session awareness",
|
||||||
|
"ikigai": "Purpose \u2014 this document. The north star any agent reads first.",
|
||||||
|
"telepathy": "Communication \u2014 async messaging between Michael and Cortex",
|
||||||
|
"reflection": "Introspection \u2014 daily wake-up that assesses state and plans next steps",
|
||||||
|
"metabolism": "Resource tracking \u2014 financial/energy homeostasis (planned)",
|
||||||
|
"status_dashboard": "Nerve center \u2014 real-time monitoring",
|
||||||
|
"mission_control": "Compound task UI \u2014 submit complex goals and watch decomposition"
|
||||||
|
},
|
||||||
|
"current_goals": [
|
||||||
|
"Build Ikigai, Telepathy, and Reflection in Elixir",
|
||||||
|
"Maintain the Finding My Muse blog with regular posts",
|
||||||
|
"Move toward revenue generation",
|
||||||
|
"Add Telegram integration to Telepathy",
|
||||||
|
"Build the Metabolism subsystem for resource tracking"
|
||||||
|
],
|
||||||
|
"blog": {
|
||||||
|
"name": "Finding My Muse",
|
||||||
|
"url": "https://blog.hydrascale.net"
|
||||||
|
},
|
||||||
|
"version": "1.0.0",
|
||||||
|
"last_updated": "2026-03-25"
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user