feat(content): add About page with research focus and Te Tiriti acknowledgment
Add comprehensive About page emphasizing moral philosophy foundation over organizational theory. PluralisticDeliberationOrchestrator positioned as primary research focus. Te Tiriti o Waitangi content integrated to establish indigenous data sovereignty principles. Also implements auto-compact tracking system to gather empirical data on Claude Code context compression events, enabling future heuristic predictions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
325a06139f
commit
50a512e532
3 changed files with 1024 additions and 0 deletions
326
public/about.html
Normal file
326
public/about.html
Normal file
|
|
@ -0,0 +1,326 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>About | Tractatus AI Safety Framework</title>
|
||||||
|
<meta name="description" content="Learn about the Tractatus Framework: our mission, values, team, and commitment to preserving human agency through structural AI safety.">
|
||||||
|
<link rel="stylesheet" href="/css/fonts.css?v=1761163813">
|
||||||
|
<link rel="stylesheet" href="/css/tailwind.css?v=1761163813">
|
||||||
|
<link rel="stylesheet" href="/css/tractatus-theme.min.css?v=1761163813">
|
||||||
|
<style>
|
||||||
|
/* Accessibility: Skip link */
|
||||||
|
.skip-link { position: absolute; left: -9999px; top: 0; }
|
||||||
|
.skip-link:focus { left: 0; z-index: 100; background: white; padding: 1rem; border: 2px solid #3b82f6; }
|
||||||
|
|
||||||
|
/* Accessibility: Focus indicators (WCAG 2.4.7) */
|
||||||
|
a:focus, button:focus, input:focus, select:focus, textarea:focus {
|
||||||
|
outline: 3px solid #3b82f6;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
a:focus:not(:focus-visible) { outline: none; }
|
||||||
|
a:focus-visible { outline: 3px solid #3b82f6; outline-offset: 2px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50">
|
||||||
|
|
||||||
|
<!-- Skip Link for Keyboard Navigation -->
|
||||||
|
<a href="#main-content" class="skip-link">Skip to main content</a>
|
||||||
|
|
||||||
|
<!-- Navigation (injected by navbar.js) -->
|
||||||
|
<script src="/js/components/navbar.js?v=1761163813"></script>
|
||||||
|
|
||||||
|
<!-- Breadcrumb Navigation -->
|
||||||
|
<nav class="bg-gray-50 border-b border-gray-200 py-3" aria-label="Breadcrumb">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<ol class="flex items-center space-x-2 text-sm">
|
||||||
|
<li><a href="/" class="hover:underline transition-colors text-tractatus-link">Home</a></li>
|
||||||
|
<li class="text-gray-400">/</li>
|
||||||
|
<li class="text-gray-900 font-medium" aria-current="page">About</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<div class="bg-gradient-to-br from-blue-50 to-purple-50 py-20">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="text-5xl font-bold text-gray-900 mb-6" data-i18n="header.title">
|
||||||
|
About Tractatus
|
||||||
|
</h1>
|
||||||
|
<p class="text-xl text-gray-600 max-w-3xl mx-auto" data-i18n="header.subtitle">
|
||||||
|
A framework for AI safety through architectural constraints, preserving human agency where it matters most.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mission Section -->
|
||||||
|
<main id="main-content" class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||||
|
<section class="mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-900 mb-6" data-i18n="mission.heading">Our Mission</h2>
|
||||||
|
<div class="prose prose-lg text-gray-600">
|
||||||
|
<p class="mb-6" data-i18n="mission.democratic_legitimacy">
|
||||||
|
As AI systems make increasingly consequential decisions—medical treatment, hiring, content moderation, resource allocation—a fundamental question emerges: whose values guide these decisions? Current AI alignment approaches embed particular moral frameworks into systems deployed universally. When they work, it's because everyone affected shares those values. When they don't, someone's values inevitably override others'.
|
||||||
|
</p>
|
||||||
|
<p class="mb-6" data-i18n-html="mission.intro">
|
||||||
|
The Tractatus Framework exists to address a fundamental problem in AI safety: current approaches rely on training, fine-tuning, and corporate governance—all of which can fail, drift, or be overridden. We propose <strong>safety through architecture</strong>.
|
||||||
|
</p>
|
||||||
|
<p class="mb-6" data-i18n-html="mission.wittgenstein">
|
||||||
|
Inspired by Ludwig Wittgenstein's <em>Tractatus Logico-Philosophicus</em>, our framework recognizes that some domains—values, ethics, cultural context, human agency—cannot be systematized. What cannot be systematized must not be automated. AI systems should have structural constraints that prevent them from crossing these boundaries.
|
||||||
|
</p>
|
||||||
|
<blockquote class="border-l-4 border-blue-600 pl-6 italic text-gray-700 my-6">
|
||||||
|
<span data-i18n-html="mission.quote">"Whereof one cannot speak, thereof one must be silent."</span><br>
|
||||||
|
<span data-i18n-html="mission.quote_source">— Ludwig Wittgenstein, <em>Tractatus</em> (§7)</span>
|
||||||
|
</blockquote>
|
||||||
|
<p class="mb-4" data-i18n-html="mission.applied">
|
||||||
|
Applied to AI: <strong>"What cannot be systematized must not be automated."</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Our Research Focus -->
|
||||||
|
<section class="mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-900 mb-6">Our Research Focus</h2>
|
||||||
|
<div class="prose prose-lg text-gray-600">
|
||||||
|
<p class="mb-6">
|
||||||
|
Tractatus emerged from a simple but urgent question: as AI systems become more capable, how do we preserve human control over the decisions that matter? Not technical decisions—moral ones. Decisions about whose privacy matters more. Whose needs come first. What trade-offs are acceptable.
|
||||||
|
</p>
|
||||||
|
<p class="mb-6">
|
||||||
|
This is fundamentally a problem of <strong>moral philosophy</strong>, not management science. Different communities hold genuinely different, equally legitimate values. You cannot rank "family privacy" against "community safety" on a single scale—they are incommensurable. Any system claiming to do so is simply imposing one community's values on everyone else.
|
||||||
|
</p>
|
||||||
|
<p class="mb-6">
|
||||||
|
The framework's core insight comes from attending carefully to what humans actually need from AI: not another authority making decisions for them, but systems that recognize when a decision requires human deliberation across different perspectives. The <strong>PluralisticDeliberationOrchestrator</strong> represents our primary research focus—a component designed to detect when AI encounters values conflicts and coordinate deliberation among affected stakeholders rather than making autonomous choices.
|
||||||
|
</p>
|
||||||
|
<p class="mb-4">
|
||||||
|
Traditional organizational theory addresses authority through hierarchy. But post-AI contexts require something different: authority through appropriate deliberative process. Not "AI decides for everyone," but "AI recognizes when humans must decide together."
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Why This Matters -->
|
||||||
|
<section class="mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-900 mb-6" data-i18n="why_this_matters.heading">Why This Matters</h2>
|
||||||
|
<div class="prose prose-lg text-gray-600">
|
||||||
|
<p class="mb-6" data-i18n="why_this_matters.paragraph_1">
|
||||||
|
AI systems are amoral hierarchical constructs, fundamentally incompatible with the plural, incommensurable values human societies exhibit. A hierarchy can only impose one framework and treat conflicts as anomalies. You cannot pattern-match your way to pluralism.
|
||||||
|
</p>
|
||||||
|
<p class="mb-6" data-i18n="why_this_matters.paragraph_2">
|
||||||
|
Human societies spent centuries learning to navigate moral pluralism through constitutional separation of powers, federalism, subsidiarity, and deliberative democracy. These structures acknowledge that legitimate authority over value decisions belongs to affected communities, not distant experts claiming universal wisdom.
|
||||||
|
</p>
|
||||||
|
<p class="mb-6" data-i18n="why_this_matters.paragraph_3">
|
||||||
|
AI development risks reversing this progress. As capability concentrates in a few labs, value decisions affecting billions are being encoded by small teams applying their particular moral intuitions at scale. Not through malice—through structural necessity. The architecture of current AI systems demands hierarchical value frameworks.
|
||||||
|
</p>
|
||||||
|
<p class="mb-4" data-i18n="why_this_matters.paragraph_4">
|
||||||
|
The Tractatus Framework offers an alternative: separate what must be universal (safety boundaries) from what should be contextual (value deliberation). This preserves human agency over moral decisions while enabling AI capability to scale.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Core Values -->
|
||||||
|
<section class="mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-900 mb-6" data-i18n="core_values.heading">Core Values</h2>
|
||||||
|
|
||||||
|
<!-- Te Tiriti Summary -->
|
||||||
|
<div class="bg-blue-50 border-l-4 border-blue-600 p-6 rounded-r-lg mb-8">
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mb-3">Digital Sovereignty & Te Tiriti o Waitangi</h3>
|
||||||
|
<p class="text-gray-700 mb-3">
|
||||||
|
The principle that communities should control their own data and technology isn't new—it has deep roots in indigenous frameworks that predate Western tech by centuries. The Tractatus Framework is developed in Aotearoa New Zealand, and we recognize Te Tiriti o Waitangi (the Treaty of Waitangi, 1840) as establishing principles of partnership, protection, and participation that directly inform how we think about AI sovereignty.
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-700">
|
||||||
|
This isn't performative acknowledgment. Concepts like <em>rangatiratanga</em> (self-determination), <em>kaitiakitanga</em> (guardianship), and <em>mana</em> (authority and dignity) provide concrete guidance for building AI systems that respect human agency across cultural contexts. <a href="/about/values.html#te-tiriti" class="text-blue-600 hover:text-blue-700 font-medium underline">Read our complete approach to Te Tiriti and indigenous data sovereignty →</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div class="bg-white rounded-lg shadow-sm p-6 border-l-4 border-green-600">
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mb-3" data-i18n="core_values.sovereignty_title">Sovereignty</h3>
|
||||||
|
<p class="text-gray-600" data-i18n="core_values.sovereignty_desc">
|
||||||
|
Individuals and communities must maintain control over decisions affecting their data, privacy, and values. AI systems must preserve human agency, not erode it.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-sm p-6 border-l-4 border-blue-600">
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mb-3" data-i18n="core_values.transparency_title">Transparency</h3>
|
||||||
|
<p class="text-gray-600" data-i18n="core_values.transparency_desc">
|
||||||
|
All AI decisions must be explainable, auditable, and reversible. No black boxes. Users deserve to understand how and why systems make choices, and have power to override them.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-sm p-6 border-l-4 border-purple-600">
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mb-3" data-i18n="core_values.harmlessness_title">Harmlessness</h3>
|
||||||
|
<p class="text-gray-600" data-i18n="core_values.harmlessness_desc">
|
||||||
|
AI systems must not cause harm through action or inaction. This includes preventing drift, detecting degradation, and enforcing boundaries against values erosion.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-sm p-6 border-l-4 border-orange-600">
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mb-3" data-i18n="core_values.community_title">Community</h3>
|
||||||
|
<p class="text-gray-600" data-i18n="core_values.community_desc">
|
||||||
|
AI safety is a collective endeavor. We are committed to open collaboration, knowledge sharing, and empowering communities to shape the AI systems that affect their lives.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-sm p-6 border-l-4 border-teal-600">
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mb-3" data-i18n="core_values.pluralism_title">Pluralism</h3>
|
||||||
|
<p class="text-gray-600" data-i18n="core_values.pluralism_desc">
|
||||||
|
Different communities hold different, equally legitimate values. AI systems must respect this pluralism structurally, not by pretending one framework can serve all contexts. Value decisions require deliberation among affected stakeholders, not autonomous AI choices.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8 text-center">
|
||||||
|
<a href="/about/values.html" class="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-blue-700 transition" data-i18n="core_values.read_values_btn">
|
||||||
|
Read Our Complete Values Statement →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- How It Works -->
|
||||||
|
<section class="mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-900 mb-6" data-i18n="how_it_works.heading">How It Works</h2>
|
||||||
|
<div class="prose prose-lg text-gray-600">
|
||||||
|
<p class="mb-6" data-i18n="how_it_works.intro">
|
||||||
|
The Tractatus Framework consists of six integrated components that work together to enforce structural safety:
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
|
||||||
|
<div class="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900 mb-2" data-i18n="how_it_works.classifier_title">InstructionPersistenceClassifier</h3>
|
||||||
|
<p class="text-sm text-gray-600" data-i18n="how_it_works.classifier_desc">
|
||||||
|
Classifies instructions by quadrant (Strategic, Operational, Tactical, System, Stochastic) and determines persistence level (HIGH/MEDIUM/LOW/VARIABLE).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900 mb-2" data-i18n="how_it_works.validator_title">CrossReferenceValidator</h3>
|
||||||
|
<p class="text-sm text-gray-600" data-i18n="how_it_works.validator_desc">
|
||||||
|
Validates AI actions against stored instructions to prevent pattern recognition bias (like the 27027 incident where AI's training patterns immediately overrode user's explicit "port 27027" instruction).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900 mb-2" data-i18n="how_it_works.boundary_title">BoundaryEnforcer</h3>
|
||||||
|
<p class="text-sm text-gray-600" data-i18n="how_it_works.boundary_desc">
|
||||||
|
Ensures AI never makes values decisions without human approval. Privacy trade-offs, user agency, cultural context—these require human judgment.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900 mb-2" data-i18n="how_it_works.pressure_title">ContextPressureMonitor</h3>
|
||||||
|
<p class="text-sm text-gray-600" data-i18n="how_it_works.pressure_desc">
|
||||||
|
Detects when session conditions increase error probability (token pressure, message length, task complexity) and adjusts behavior or suggests handoff.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900 mb-2" data-i18n="how_it_works.metacognitive_title">MetacognitiveVerifier</h3>
|
||||||
|
<p class="text-sm text-gray-600" data-i18n="how_it_works.metacognitive_desc">
|
||||||
|
AI self-checks complex reasoning before proposing actions. Evaluates alignment, coherence, completeness, safety, and alternatives.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-gray-50 rounded-lg p-6">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900 mb-2" data-i18n="how_it_works.deliberation_title">PluralisticDeliberationOrchestrator</h3>
|
||||||
|
<p class="text-sm text-gray-600" data-i18n="how_it_works.deliberation_desc">
|
||||||
|
When AI encounters values decisions—choices with no single "correct" answer—coordinates deliberation among affected stakeholders rather than making autonomous choices. Preserves human agency over moral decisions.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8 text-center">
|
||||||
|
<a href="/implementer.html" class="inline-block bg-purple-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-purple-700 transition" data-i18n="how_it_works.read_technical_btn">
|
||||||
|
Read Technical Documentation & Implementation Guide →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Origin Story -->
|
||||||
|
<section class="mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-900 mb-6" data-i18n="origin_story.heading">Origin Story</h2>
|
||||||
|
<div class="prose prose-lg text-gray-600">
|
||||||
|
<p class="mb-6" data-i18n="origin_story.paragraph_1">
|
||||||
|
The Tractatus Framework emerged from real-world AI failures experienced during extended Claude Code sessions. The "27027 incident"—where AI's training patterns immediately overrode an explicit instruction (user said "port 27027", AI used "port 27017")—revealed that traditional safety approaches were insufficient. This wasn't forgetting; it was pattern recognition bias autocorrecting the user.
|
||||||
|
</p>
|
||||||
|
<p class="mb-6" data-i18n="origin_story.paragraph_2">
|
||||||
|
After documenting multiple failure modes (pattern recognition bias, values drift, silent degradation), we recognized a pattern: AI systems lacked structural constraints. They could theoretically "learn" safety, but in practice their training patterns overrode explicit instructions, and the problem gets worse as capabilities increase.
|
||||||
|
</p>
|
||||||
|
<p class="mb-4" data-i18n="origin_story.paragraph_3">
|
||||||
|
The solution wasn't better training—it was architecture. Drawing inspiration from Wittgenstein's insight that some things lie beyond the limits of language (and thus systematization), we built a framework that enforces boundaries through structure, not aspiration.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- License & Contribution -->
|
||||||
|
<section class="mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-900 mb-6" data-i18n="license.heading">License & Contribution</h2>
|
||||||
|
<div class="prose prose-lg text-gray-600">
|
||||||
|
<p class="mb-4" data-i18n-html="license.intro">
|
||||||
|
The Tractatus Framework is open source under the <strong>Apache License 2.0</strong>. We encourage:
|
||||||
|
</p>
|
||||||
|
<ul class="mb-6">
|
||||||
|
<li data-i18n="license.encouragement_1">Academic research and validation studies</li>
|
||||||
|
<li data-i18n="license.encouragement_2">Implementation in production AI systems</li>
|
||||||
|
<li data-i18n="license.encouragement_3">Submission of failure case studies</li>
|
||||||
|
<li data-i18n="license.encouragement_4">Theoretical extensions and improvements</li>
|
||||||
|
<li data-i18n="license.encouragement_5">Community collaboration and knowledge sharing</li>
|
||||||
|
</ul>
|
||||||
|
<p class="mb-8" data-i18n="license.rationale">
|
||||||
|
The framework is intentionally permissive because AI safety benefits from transparency and collective improvement, not proprietary control.
|
||||||
|
</p>
|
||||||
|
<h3 class="text-xl font-bold text-gray-900 mt-8 mb-4" data-i18n="license.why_apache_title">Why Apache 2.0?</h3>
|
||||||
|
<p class="mb-4" data-i18n="license.why_apache_intro">
|
||||||
|
We chose Apache 2.0 over MIT because it provides:
|
||||||
|
</p>
|
||||||
|
<ul class="mb-6">
|
||||||
|
<li data-i18n-html="license.patent_protection"><strong>Patent Protection:</strong> Explicit patent grant protects users from patent litigation by contributors</li>
|
||||||
|
<li data-i18n-html="license.contributor_clarity"><strong>Contributor Clarity:</strong> Clear terms for how contributions are licensed</li>
|
||||||
|
<li data-i18n-html="license.permissive_use"><strong>Permissive Use:</strong> Like MIT, allows commercial use and inclusion in proprietary products</li>
|
||||||
|
<li data-i18n-html="license.community_standard"><strong>Community Standard:</strong> Widely used in AI/ML projects (TensorFlow, PyTorch, Apache Spark)</li>
|
||||||
|
</ul>
|
||||||
|
<p class="mt-4">
|
||||||
|
<a href="/LICENSE" class="text-blue-600 hover:text-blue-700 font-medium" data-i18n="license.view_license_link">View full Apache 2.0 License →</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CTA Section -->
|
||||||
|
<div class="bg-gradient-to-r from-blue-600 to-purple-600 py-16">
|
||||||
|
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-white">
|
||||||
|
<h2 class="text-3xl font-bold mb-4" data-i18n="cta.title">Join the Movement</h2>
|
||||||
|
<p class="text-xl mb-8 opacity-90" data-i18n="cta.description">
|
||||||
|
Help build AI systems that preserve human agency through architectural constraints.
|
||||||
|
</p>
|
||||||
|
<div class="flex justify-center space-x-4">
|
||||||
|
<a href="/researcher.html" class="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-gray-100 transition" data-i18n="cta.for_researchers_btn">
|
||||||
|
For Researchers
|
||||||
|
</a>
|
||||||
|
<a href="/implementer.html" class="bg-blue-700 text-white px-8 py-3 rounded-lg font-semibold hover:bg-blue-800 transition border-2 border-white" data-i18n="cta.for_implementers_btn">
|
||||||
|
For Implementers
|
||||||
|
</a>
|
||||||
|
<a href="/leader.html" class="bg-blue-700 text-white px-8 py-3 rounded-lg font-semibold hover:bg-blue-800 transition border-2 border-white" data-i18n="cta.for_leaders_btn">
|
||||||
|
For Leaders
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer with Te Tiriti Acknowledgment -->
|
||||||
|
<!-- Footer -->
|
||||||
|
<!-- Internationalization -->
|
||||||
|
<script src="/js/i18n-simple.js?v=1761163813"></script>
|
||||||
|
<script src="/js/components/language-selector.js?v=1761163813"></script>
|
||||||
|
|
||||||
|
<!-- Scroll Animations (Phase 3) -->
|
||||||
|
<script src="/js/scroll-animations.js?v=1761163813"></script>
|
||||||
|
<!-- Page Transitions (Phase 3) -->
|
||||||
|
<script src="/js/page-transitions.js?v=1761163813"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Footer Component -->
|
||||||
|
<script src="/js/components/footer.js?v=1761163813"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
438
scripts/check-session-pressure.js
Executable file
438
scripts/check-session-pressure.js
Executable file
|
|
@ -0,0 +1,438 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Session Pressure Monitor Script
|
||||||
|
*
|
||||||
|
* Uses ContextPressureMonitor to analyze current session state and provide
|
||||||
|
* recommendations for session management.
|
||||||
|
*
|
||||||
|
* This script demonstrates the Tractatus framework dogfooding itself - using
|
||||||
|
* its own governance services to manage AI-assisted development sessions.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node scripts/check-session-pressure.js [options]
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
* --tokens <current>/<budget> Current token usage (e.g., 89195/200000)
|
||||||
|
* --messages <count> Number of messages in conversation
|
||||||
|
* --tasks <count> Number of active tasks
|
||||||
|
* --errors <count> Recent errors in last 10 minutes
|
||||||
|
* --json Output JSON format
|
||||||
|
* --verbose Show detailed analysis
|
||||||
|
*/
|
||||||
|
|
||||||
|
const monitor = require('../src/services/ContextPressureMonitor.service');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const SESSION_STATE_PATH = path.join(__dirname, '../.claude/session-state.json');
|
||||||
|
|
||||||
|
// Load session state to get compact history
|
||||||
|
function loadSessionState() {
|
||||||
|
if (!fs.existsSync(SESSION_STATE_PATH)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return JSON.parse(fs.readFileSync(SESSION_STATE_PATH, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyze compact event patterns
|
||||||
|
function analyzeCompactHistory(sessionState) {
|
||||||
|
if (!sessionState || !sessionState.auto_compact_events || sessionState.auto_compact_events.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const events = sessionState.auto_compact_events;
|
||||||
|
|
||||||
|
// Calculate averages
|
||||||
|
const avgTokens = events.reduce((sum, e) => sum + e.metrics.tokens_at_compact, 0) / events.length;
|
||||||
|
const avgMessages = events.reduce((sum, e) => sum + e.metrics.messages, 0) / events.length;
|
||||||
|
const avgActions = events.reduce((sum, e) => sum + e.metrics.action_count, 0) / events.length;
|
||||||
|
const avgDuration = events.reduce((sum, e) => sum + e.metrics.session_duration_minutes, 0) / events.length;
|
||||||
|
|
||||||
|
// Pressure levels at compact
|
||||||
|
const pressureLevels = events.map(e => e.metrics.pressure_level);
|
||||||
|
const pressureScores = events.filter(e => e.metrics.pressure_score !== null)
|
||||||
|
.map(e => e.metrics.pressure_score);
|
||||||
|
const avgPressureScore = pressureScores.length > 0
|
||||||
|
? pressureScores.reduce((sum, s) => sum + s, 0) / pressureScores.length
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// Token percentage distribution
|
||||||
|
const tokenPercentages = events.map(e => parseFloat(e.metrics.token_percentage));
|
||||||
|
const avgTokenPercentage = tokenPercentages.reduce((sum, p) => sum + p, 0) / tokenPercentages.length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
total_compacts: events.length,
|
||||||
|
averages: {
|
||||||
|
tokens: Math.round(avgTokens),
|
||||||
|
messages: Math.round(avgMessages),
|
||||||
|
actions: Math.round(avgActions),
|
||||||
|
duration_minutes: Math.round(avgDuration),
|
||||||
|
token_percentage: avgTokenPercentage.toFixed(1),
|
||||||
|
pressure_score: avgPressureScore !== null ? (avgPressureScore * 100).toFixed(1) : null
|
||||||
|
},
|
||||||
|
pressure_levels: pressureLevels,
|
||||||
|
latest_compact: events[events.length - 1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Predict compact risk based on historical data
|
||||||
|
function predictCompactRisk(currentMetrics, compactHistory) {
|
||||||
|
if (!compactHistory) {
|
||||||
|
return { level: 'UNKNOWN', confidence: 0, factors: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const factors = [];
|
||||||
|
let riskScore = 0;
|
||||||
|
|
||||||
|
// Token proximity to average compact point
|
||||||
|
if (currentMetrics.tokens && compactHistory.averages.tokens) {
|
||||||
|
const tokenProximity = currentMetrics.tokens / compactHistory.averages.tokens;
|
||||||
|
if (tokenProximity > 0.9) {
|
||||||
|
riskScore += 30;
|
||||||
|
factors.push(`Tokens near historical compact point (${(tokenProximity * 100).toFixed(0)}%)`);
|
||||||
|
} else if (tokenProximity > 0.8) {
|
||||||
|
riskScore += 15;
|
||||||
|
factors.push(`Tokens approaching compact threshold (${(tokenProximity * 100).toFixed(0)}%)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message proximity to average compact point
|
||||||
|
if (currentMetrics.messages && compactHistory.averages.messages) {
|
||||||
|
const messageProximity = currentMetrics.messages / compactHistory.averages.messages;
|
||||||
|
if (messageProximity > 0.9) {
|
||||||
|
riskScore += 30;
|
||||||
|
factors.push(`Messages near historical compact point (${(messageProximity * 100).toFixed(0)}%)`);
|
||||||
|
} else if (messageProximity > 0.8) {
|
||||||
|
riskScore += 15;
|
||||||
|
factors.push(`Messages approaching compact threshold (${(messageProximity * 100).toFixed(0)}%)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action count proximity
|
||||||
|
if (currentMetrics.actions && compactHistory.averages.actions) {
|
||||||
|
const actionProximity = currentMetrics.actions / compactHistory.averages.actions;
|
||||||
|
if (actionProximity > 0.9) {
|
||||||
|
riskScore += 20;
|
||||||
|
factors.push(`Actions near historical compact point (${(actionProximity * 100).toFixed(0)}%)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine risk level
|
||||||
|
let level;
|
||||||
|
if (riskScore >= 60) level = 'HIGH';
|
||||||
|
else if (riskScore >= 30) level = 'MEDIUM';
|
||||||
|
else level = 'LOW';
|
||||||
|
|
||||||
|
return {
|
||||||
|
level,
|
||||||
|
score: riskScore,
|
||||||
|
confidence: Math.min(compactHistory.total_compacts * 20, 100), // More data = more confidence
|
||||||
|
factors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse command line arguments
|
||||||
|
function parseArgs() {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const options = {
|
||||||
|
tokenUsage: null,
|
||||||
|
tokenBudget: null,
|
||||||
|
messages: 0,
|
||||||
|
tasks: 1,
|
||||||
|
errors: 0,
|
||||||
|
json: false,
|
||||||
|
verbose: false
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
switch (args[i]) {
|
||||||
|
case '--tokens':
|
||||||
|
const [current, budget] = args[++i].split('/').map(Number);
|
||||||
|
options.tokenUsage = current;
|
||||||
|
options.tokenBudget = budget;
|
||||||
|
break;
|
||||||
|
case '--messages':
|
||||||
|
options.messages = parseInt(args[++i]);
|
||||||
|
break;
|
||||||
|
case '--tasks':
|
||||||
|
options.tasks = parseInt(args[++i]);
|
||||||
|
break;
|
||||||
|
case '--errors':
|
||||||
|
options.errors = parseInt(args[++i]);
|
||||||
|
break;
|
||||||
|
case '--json':
|
||||||
|
options.json = true;
|
||||||
|
break;
|
||||||
|
case '--verbose':
|
||||||
|
options.verbose = true;
|
||||||
|
break;
|
||||||
|
case '--help':
|
||||||
|
console.log(`
|
||||||
|
Session Pressure Monitor - Tractatus Framework
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
node scripts/check-session-pressure.js [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--tokens <current>/<budget> Token usage (e.g., 89195/200000)
|
||||||
|
--messages <count> Conversation length
|
||||||
|
--tasks <count> Active tasks
|
||||||
|
--errors <count> Recent errors
|
||||||
|
--json JSON output
|
||||||
|
--verbose Detailed analysis
|
||||||
|
--help Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
# Check current session
|
||||||
|
node scripts/check-session-pressure.js --tokens 89195/200000 --messages 28 --tasks 2
|
||||||
|
|
||||||
|
# JSON output for automation
|
||||||
|
node scripts/check-session-pressure.js --tokens 150000/200000 --json
|
||||||
|
|
||||||
|
# Verbose analysis
|
||||||
|
node scripts/check-session-pressure.js --tokens 180000/200000 --messages 50 --verbose
|
||||||
|
`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format pressure level with color
|
||||||
|
function formatLevel(level) {
|
||||||
|
const colors = {
|
||||||
|
NORMAL: '\x1b[32m', // Green
|
||||||
|
ELEVATED: '\x1b[33m', // Yellow
|
||||||
|
HIGH: '\x1b[35m', // Magenta
|
||||||
|
CRITICAL: '\x1b[31m', // Red
|
||||||
|
DANGEROUS: '\x1b[41m' // Red background
|
||||||
|
};
|
||||||
|
const reset = '\x1b[0m';
|
||||||
|
return `${colors[level] || ''}${level}${reset}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format recommendation with icon
|
||||||
|
function formatRecommendation(rec) {
|
||||||
|
const icons = {
|
||||||
|
CONTINUE_NORMAL: '✅',
|
||||||
|
INCREASE_VERIFICATION: '⚠️',
|
||||||
|
SUGGEST_CONTEXT_REFRESH: '🔄',
|
||||||
|
MANDATORY_VERIFICATION: '🚨',
|
||||||
|
IMMEDIATE_HALT: '🛑'
|
||||||
|
};
|
||||||
|
return `${icons[rec] || '•'} ${rec}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main analysis function
|
||||||
|
function analyzeSession(options) {
|
||||||
|
// Build context object
|
||||||
|
const context = {
|
||||||
|
messages_count: options.messages,
|
||||||
|
task_depth: options.tasks,
|
||||||
|
errors_recent: options.errors
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add token usage if provided
|
||||||
|
if (options.tokenUsage && options.tokenBudget) {
|
||||||
|
context.token_usage = options.tokenUsage / options.tokenBudget;
|
||||||
|
context.token_limit = options.tokenBudget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load session state and compact history
|
||||||
|
const sessionState = loadSessionState();
|
||||||
|
const compactHistory = sessionState ? analyzeCompactHistory(sessionState) : null;
|
||||||
|
|
||||||
|
// Predict compact risk
|
||||||
|
const compactRisk = compactHistory ? predictCompactRisk({
|
||||||
|
tokens: options.tokenUsage,
|
||||||
|
messages: options.messages,
|
||||||
|
actions: sessionState?.action_count || 0
|
||||||
|
}, compactHistory) : null;
|
||||||
|
|
||||||
|
// Run analysis
|
||||||
|
const analysis = monitor.analyzePressure(context);
|
||||||
|
analysis.compactHistory = compactHistory;
|
||||||
|
analysis.compactRisk = compactRisk;
|
||||||
|
|
||||||
|
// Output results
|
||||||
|
if (options.json) {
|
||||||
|
console.log(JSON.stringify(analysis, null, 2));
|
||||||
|
} else {
|
||||||
|
console.log('\n╔════════════════════════════════════════════════════════════════╗');
|
||||||
|
console.log('║ Tractatus Session Pressure Analysis ║');
|
||||||
|
console.log('╚════════════════════════════════════════════════════════════════╝\n');
|
||||||
|
|
||||||
|
// Pressure Level
|
||||||
|
console.log(`Pressure Level: ${formatLevel(analysis.level)}`);
|
||||||
|
console.log(`Overall Score: ${(analysis.overall_score * 100).toFixed(1)}%`);
|
||||||
|
console.log(`Action: ${analysis.action}\n`);
|
||||||
|
|
||||||
|
// Metrics
|
||||||
|
console.log('Metrics:');
|
||||||
|
console.log(` Token Usage: ${(analysis.metrics.tokenUsage.score * 100).toFixed(1)}%`);
|
||||||
|
console.log(` Conversation: ${(analysis.metrics.conversationLength.score * 100).toFixed(1)}%`);
|
||||||
|
console.log(` Task Complexity: ${(analysis.metrics.taskComplexity.score * 100).toFixed(1)}%`);
|
||||||
|
console.log(` Error Frequency: ${(analysis.metrics.errorFrequency.score * 100).toFixed(1)}%`);
|
||||||
|
console.log(` Instructions: ${(analysis.metrics.instructionDensity.score * 100).toFixed(1)}%\n`);
|
||||||
|
|
||||||
|
// Recommendations
|
||||||
|
if (analysis.recommendations.length > 0) {
|
||||||
|
console.log('Recommendations:');
|
||||||
|
analysis.recommendations.forEach(rec => {
|
||||||
|
console.log(` ${formatRecommendation(rec)}`);
|
||||||
|
});
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnings
|
||||||
|
if (analysis.warnings.length > 0) {
|
||||||
|
console.log('⚠️ Warnings:');
|
||||||
|
analysis.warnings.forEach(warning => {
|
||||||
|
console.log(` • ${warning}`);
|
||||||
|
});
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trend
|
||||||
|
if (analysis.trend) {
|
||||||
|
const trendIcons = {
|
||||||
|
escalating: '📈 Escalating',
|
||||||
|
improving: '📉 Improving',
|
||||||
|
stable: '➡️ Stable'
|
||||||
|
};
|
||||||
|
console.log(`Trend: ${trendIcons[analysis.trend]}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact History and Risk Prediction
|
||||||
|
if (analysis.compactHistory) {
|
||||||
|
console.log('═════════════════════════════════════════════════════════════════');
|
||||||
|
console.log('📊 Auto-Compact History\n');
|
||||||
|
console.log(`Total Compacts Recorded: ${analysis.compactHistory.total_compacts}`);
|
||||||
|
console.log('\nAverage Metrics at Compact:');
|
||||||
|
console.log(` Tokens: ${analysis.compactHistory.averages.tokens} (${analysis.compactHistory.averages.token_percentage}% of budget)`);
|
||||||
|
console.log(` Messages: ${analysis.compactHistory.averages.messages}`);
|
||||||
|
console.log(` Actions: ${analysis.compactHistory.averages.actions}`);
|
||||||
|
console.log(` Session Length: ${analysis.compactHistory.averages.duration_minutes} minutes`);
|
||||||
|
if (analysis.compactHistory.averages.pressure_score) {
|
||||||
|
console.log(` Pressure Score: ${analysis.compactHistory.averages.pressure_score}%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Latest compact info
|
||||||
|
const latest = analysis.compactHistory.latest_compact;
|
||||||
|
const timeSince = Math.round((Date.now() - new Date(latest.timestamp).getTime()) / (1000 * 60));
|
||||||
|
console.log(`\nLast Compact: ${timeSince} minutes ago`);
|
||||||
|
if (latest.note) {
|
||||||
|
console.log(` Note: ${latest.note}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
// Compact risk prediction
|
||||||
|
if (analysis.compactRisk && analysis.compactRisk.level !== 'UNKNOWN') {
|
||||||
|
const riskIcons = {
|
||||||
|
LOW: '✅',
|
||||||
|
MEDIUM: '⚠️',
|
||||||
|
HIGH: '🚨'
|
||||||
|
};
|
||||||
|
const riskColors = {
|
||||||
|
LOW: '\x1b[32m', // Green
|
||||||
|
MEDIUM: '\x1b[33m', // Yellow
|
||||||
|
HIGH: '\x1b[31m' // Red
|
||||||
|
};
|
||||||
|
const reset = '\x1b[0m';
|
||||||
|
|
||||||
|
console.log('🔮 Compact Risk Prediction\n');
|
||||||
|
console.log(`Risk Level: ${riskIcons[analysis.compactRisk.level]} ${riskColors[analysis.compactRisk.level]}${analysis.compactRisk.level}${reset}`);
|
||||||
|
console.log(`Risk Score: ${analysis.compactRisk.score}/100`);
|
||||||
|
console.log(`Confidence: ${analysis.compactRisk.confidence}% (based on ${analysis.compactHistory.total_compacts} recorded events)`);
|
||||||
|
|
||||||
|
if (analysis.compactRisk.factors.length > 0) {
|
||||||
|
console.log('\nRisk Factors:');
|
||||||
|
analysis.compactRisk.factors.forEach(factor => {
|
||||||
|
console.log(` • ${factor}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
// Warning if high risk
|
||||||
|
if (analysis.compactRisk.level === 'HIGH') {
|
||||||
|
console.log('⚠️ HIGH COMPACT RISK: Consider summarizing progress and planning next steps');
|
||||||
|
console.log(' to minimize context loss if compaction occurs.\n');
|
||||||
|
} else if (analysis.compactRisk.level === 'MEDIUM') {
|
||||||
|
console.log('📝 MEDIUM COMPACT RISK: Compact may occur soon. Be prepared to\n');
|
||||||
|
console.log(' summarize key decisions and action items.\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('═════════════════════════════════════════════════════════════════\n');
|
||||||
|
} else {
|
||||||
|
console.log('═════════════════════════════════════════════════════════════════');
|
||||||
|
console.log('📊 Auto-Compact History\n');
|
||||||
|
console.log('No compact events recorded yet.');
|
||||||
|
console.log('\nWhen you notice an auto-compact (context compression), run:');
|
||||||
|
console.log(' node scripts/record-auto-compact.js --tokens <current>/<budget>\n');
|
||||||
|
console.log('This builds empirical data to predict future compacts.\n');
|
||||||
|
console.log('═════════════════════════════════════════════════════════════════\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verbose output
|
||||||
|
if (options.verbose) {
|
||||||
|
console.log('Detailed Metrics:');
|
||||||
|
Object.entries(analysis.metrics).forEach(([name, metric]) => {
|
||||||
|
console.log(` ${name}:`);
|
||||||
|
console.log(` Raw: ${metric.raw}`);
|
||||||
|
console.log(` Normalized: ${metric.normalized.toFixed(3)}`);
|
||||||
|
console.log(` Threshold: ${metric.threshold}`);
|
||||||
|
if (metric.factors) {
|
||||||
|
console.log(` Factors: ${metric.factors.join(', ')}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
console.log('─────────────────────────────────────────────────────────────────');
|
||||||
|
if (analysis.level === 'NORMAL') {
|
||||||
|
console.log('✅ Session conditions are normal. Continue working.\n');
|
||||||
|
} else if (analysis.level === 'ELEVATED') {
|
||||||
|
console.log('⚠️ Pressure is elevated. Increase verification and monitoring.\n');
|
||||||
|
} else if (analysis.level === 'HIGH') {
|
||||||
|
console.log('🔄 Pressure is high. Consider refreshing context soon.\n');
|
||||||
|
} else if (analysis.level === 'CRITICAL') {
|
||||||
|
console.log('🚨 Critical pressure! Mandatory verification required.\n');
|
||||||
|
} else if (analysis.level === 'DANGEROUS') {
|
||||||
|
console.log('🛑 DANGEROUS conditions! Halt and refresh context immediately.\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run if called directly
|
||||||
|
if (require.main === module) {
|
||||||
|
const options = parseArgs();
|
||||||
|
|
||||||
|
// Validate inputs
|
||||||
|
if (options.tokenUsage === null) {
|
||||||
|
console.error('Error: --tokens argument required');
|
||||||
|
console.error('Usage: node scripts/check-session-pressure.js --tokens <current>/<budget>');
|
||||||
|
console.error('Run with --help for more information');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const analysis = analyzeSession(options);
|
||||||
|
|
||||||
|
// Exit with appropriate code
|
||||||
|
const exitCodes = {
|
||||||
|
NORMAL: 0,
|
||||||
|
ELEVATED: 0,
|
||||||
|
HIGH: 1,
|
||||||
|
CRITICAL: 2,
|
||||||
|
DANGEROUS: 3
|
||||||
|
};
|
||||||
|
process.exit(exitCodes[analysis.level] || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { analyzeSession, parseArgs };
|
||||||
260
scripts/record-auto-compact.js
Executable file
260
scripts/record-auto-compact.js
Executable file
|
|
@ -0,0 +1,260 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Auto-Compact Event Recorder
|
||||||
|
*
|
||||||
|
* Records observable metrics when an auto-compact (context window compression)
|
||||||
|
* occurs in Claude Code sessions. This helps build empirical understanding of
|
||||||
|
* what triggers compaction since we cannot directly measure Claude's internal
|
||||||
|
* context window consumption.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node scripts/record-auto-compact.js [options]
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
* --tokens <current>/<budget> Token count from system reminder (e.g., 89195/200000)
|
||||||
|
* --messages <count> Message count from system reminder
|
||||||
|
* --note "message" Optional note about what was happening
|
||||||
|
* --auto Automatic detection (no prompts)
|
||||||
|
*
|
||||||
|
* This script should be run IMMEDIATELY AFTER noticing a compact event:
|
||||||
|
* - Session summary message appears
|
||||||
|
* - Context seems to reset
|
||||||
|
* - Previous conversation details become unavailable
|
||||||
|
*
|
||||||
|
* The goal is to build a dataset correlating observable metrics with actual
|
||||||
|
* compact events, which can later inform heuristic warnings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const readline = require('readline');
|
||||||
|
|
||||||
|
const SESSION_STATE_PATH = path.join(__dirname, '../.claude/session-state.json');
|
||||||
|
|
||||||
|
// Parse command line arguments
|
||||||
|
function parseArgs() {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const options = {
|
||||||
|
tokens: null,
|
||||||
|
budget: null,
|
||||||
|
messages: null,
|
||||||
|
note: null,
|
||||||
|
auto: false
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
switch (args[i]) {
|
||||||
|
case '--tokens':
|
||||||
|
const [current, budget] = args[++i].split('/').map(s => parseInt(s.trim()));
|
||||||
|
options.tokens = current;
|
||||||
|
options.budget = budget;
|
||||||
|
break;
|
||||||
|
case '--messages':
|
||||||
|
options.messages = parseInt(args[++i]);
|
||||||
|
break;
|
||||||
|
case '--note':
|
||||||
|
options.note = args[++i];
|
||||||
|
break;
|
||||||
|
case '--auto':
|
||||||
|
options.auto = true;
|
||||||
|
break;
|
||||||
|
case '--help':
|
||||||
|
console.log(`
|
||||||
|
Auto-Compact Event Recorder - Tractatus Framework
|
||||||
|
|
||||||
|
This tool records observable metrics when Claude Code auto-compaction occurs.
|
||||||
|
Run IMMEDIATELY after noticing a compact event to capture accurate data.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
node scripts/record-auto-compact.js [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--tokens <current>/<budget> Token usage (e.g., 89195/200000)
|
||||||
|
--messages <count> Message count
|
||||||
|
--note "message" What were you doing when compact occurred
|
||||||
|
--auto Non-interactive mode
|
||||||
|
--help Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
# Interactive mode (prompts for missing values)
|
||||||
|
node scripts/record-auto-compact.js --tokens 150000/200000
|
||||||
|
|
||||||
|
# Fully specified
|
||||||
|
node scripts/record-auto-compact.js --tokens 145000/200000 --messages 45 --note "Large file reads"
|
||||||
|
|
||||||
|
# Automatic mode (use current session state)
|
||||||
|
node scripts/record-auto-compact.js --auto
|
||||||
|
`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load session state
|
||||||
|
function loadSessionState() {
|
||||||
|
if (!fs.existsSync(SESSION_STATE_PATH)) {
|
||||||
|
console.error(`Error: Session state not found at ${SESSION_STATE_PATH}`);
|
||||||
|
console.error('Run: node scripts/session-init.js');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(fs.readFileSync(SESSION_STATE_PATH, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save session state
|
||||||
|
function saveSessionState(state) {
|
||||||
|
fs.writeFileSync(SESSION_STATE_PATH, JSON.stringify(state, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt user for input
|
||||||
|
async function promptFor(question, defaultValue = null) {
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
||||||
|
rl.question(prompt, (answer) => {
|
||||||
|
rl.close();
|
||||||
|
resolve(answer.trim() || defaultValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate session duration
|
||||||
|
function calculateDuration(startTime) {
|
||||||
|
const start = new Date(startTime);
|
||||||
|
const now = new Date();
|
||||||
|
const diffMs = now - start;
|
||||||
|
const hours = Math.floor(diffMs / (1000 * 60 * 60));
|
||||||
|
const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
return { hours, minutes, totalMinutes: Math.floor(diffMs / (1000 * 60)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main recording function
|
||||||
|
async function recordCompactEvent(options) {
|
||||||
|
console.log('\n╔════════════════════════════════════════════════════════════════╗');
|
||||||
|
console.log('║ Auto-Compact Event Recorder ║');
|
||||||
|
console.log('╚════════════════════════════════════════════════════════════════╝\n');
|
||||||
|
|
||||||
|
// Load current session state
|
||||||
|
const sessionState = loadSessionState();
|
||||||
|
console.log(`Session ID: ${sessionState.session_id}`);
|
||||||
|
console.log(`Started: ${new Date(sessionState.started).toLocaleString()}\n`);
|
||||||
|
|
||||||
|
// Initialize auto_compact_events array if not present
|
||||||
|
if (!sessionState.auto_compact_events) {
|
||||||
|
sessionState.auto_compact_events = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate session duration
|
||||||
|
const duration = calculateDuration(sessionState.started);
|
||||||
|
console.log(`Session Duration: ${duration.hours}h ${duration.minutes}m\n`);
|
||||||
|
|
||||||
|
// Gather metrics (prompt if not provided)
|
||||||
|
let tokens = options.tokens;
|
||||||
|
let budget = options.budget;
|
||||||
|
let messages = options.messages;
|
||||||
|
let note = options.note;
|
||||||
|
|
||||||
|
if (!options.auto) {
|
||||||
|
if (tokens === null) {
|
||||||
|
const tokenInput = await promptFor('Token usage (format: current/budget)',
|
||||||
|
`${sessionState.token_estimate}/${sessionState.staleness_thresholds.tokens || 200000}`);
|
||||||
|
[tokens, budget] = tokenInput.split('/').map(s => parseInt(s.trim()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages === null) {
|
||||||
|
const messageInput = await promptFor('Message count', sessionState.message_count);
|
||||||
|
messages = parseInt(messageInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note === null) {
|
||||||
|
note = await promptFor('What were you doing? (optional)', null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Auto mode: use session state
|
||||||
|
tokens = sessionState.token_estimate || 0;
|
||||||
|
budget = 200000; // Default budget
|
||||||
|
messages = sessionState.message_count || 0;
|
||||||
|
note = 'Auto-detected compact';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get pressure score from last monitoring activity
|
||||||
|
const lastPressure = sessionState.last_framework_activity?.ContextPressureMonitor;
|
||||||
|
const pressureLevel = lastPressure?.last_level || 'UNKNOWN';
|
||||||
|
const pressureScore = lastPressure?.last_score || null;
|
||||||
|
|
||||||
|
// Build compact event record
|
||||||
|
const compactEvent = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
session_id: sessionState.session_id,
|
||||||
|
metrics: {
|
||||||
|
tokens_at_compact: tokens,
|
||||||
|
token_budget: budget,
|
||||||
|
token_percentage: budget > 0 ? ((tokens / budget) * 100).toFixed(1) : null,
|
||||||
|
messages: messages,
|
||||||
|
action_count: sessionState.action_count || 0,
|
||||||
|
session_duration_minutes: duration.totalMinutes,
|
||||||
|
pressure_level: pressureLevel,
|
||||||
|
pressure_score: pressureScore
|
||||||
|
},
|
||||||
|
framework_activity: {
|
||||||
|
cross_reference_validations: sessionState.framework_components?.CrossReferenceValidator?.validations_performed || 0,
|
||||||
|
bash_command_validations: sessionState.framework_components?.BashCommandValidator?.validations_performed || 0,
|
||||||
|
bash_blocks_issued: sessionState.framework_components?.BashCommandValidator?.blocks_issued || 0
|
||||||
|
},
|
||||||
|
note: note || null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add to history
|
||||||
|
sessionState.auto_compact_events.push(compactEvent);
|
||||||
|
|
||||||
|
// Save updated state
|
||||||
|
saveSessionState(sessionState);
|
||||||
|
|
||||||
|
// Display summary
|
||||||
|
console.log('✅ Compact event recorded!\n');
|
||||||
|
console.log('Metrics Captured:');
|
||||||
|
console.log(` Tokens: ${compactEvent.metrics.tokens_at_compact} / ${compactEvent.metrics.token_budget} (${compactEvent.metrics.token_percentage}%)`);
|
||||||
|
console.log(` Messages: ${compactEvent.metrics.messages}`);
|
||||||
|
console.log(` Actions: ${compactEvent.metrics.action_count}`);
|
||||||
|
console.log(` Duration: ${duration.hours}h ${duration.minutes}m`);
|
||||||
|
console.log(` Pressure Level: ${compactEvent.metrics.pressure_level}`);
|
||||||
|
if (compactEvent.metrics.pressure_score !== null) {
|
||||||
|
console.log(` Pressure Score: ${(compactEvent.metrics.pressure_score * 100).toFixed(1)}%`);
|
||||||
|
}
|
||||||
|
console.log(`\nTotal Compacts: ${sessionState.auto_compact_events.length}\n`);
|
||||||
|
|
||||||
|
// Show compact frequency
|
||||||
|
if (sessionState.auto_compact_events.length > 1) {
|
||||||
|
const allTokens = sessionState.auto_compact_events.map(e => e.metrics.tokens_at_compact);
|
||||||
|
const avgTokens = allTokens.reduce((a, b) => a + b, 0) / allTokens.length;
|
||||||
|
|
||||||
|
const allMessages = sessionState.auto_compact_events.map(e => e.metrics.messages);
|
||||||
|
const avgMessages = allMessages.reduce((a, b) => a + b, 0) / allMessages.length;
|
||||||
|
|
||||||
|
console.log('Historical Averages:');
|
||||||
|
console.log(` Avg Tokens at Compact: ${avgTokens.toFixed(0)}`);
|
||||||
|
console.log(` Avg Messages at Compact: ${avgMessages.toFixed(1)}`);
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Data saved to:', SESSION_STATE_PATH);
|
||||||
|
console.log('\nRun: node scripts/check-session-pressure.js --tokens <current>/<budget>');
|
||||||
|
console.log(' to see compact statistics and predictions\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run if called directly
|
||||||
|
if (require.main === module) {
|
||||||
|
const options = parseArgs();
|
||||||
|
recordCompactEvent(options).catch(err => {
|
||||||
|
console.error('Error:', err.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { recordCompactEvent, loadSessionState };
|
||||||
Loading…
Add table
Reference in a new issue