diff --git a/docs/butter.html b/docs/butter.html new file mode 100644 index 00000000..b451fcb4 --- /dev/null +++ b/docs/butter.html @@ -0,0 +1,238 @@ + + + + + + + AgentPipe Butter Mode + + + + + + + +
+
+
+

Issue #1878: butter

+

Butter Mode

+

+ AgentPipe now has a dedicated butter ritual: spread, churn, melt, and clarify + task throughput until the pipeline glides like warm toast. +

+
+
🧈
+
+ +
+
+ 71% + default spread coverage for compliant toast surfaces. +
+
+ 0 crumbs + no needless runtime dependencies; this is static and shelf-stable. +
+
+ 4 states + spread, churn, melt, and clarify — the complete butter lifecycle. +
+
+ +
+

Butter controls

+
+ + + +
+
+
+
+
+ butter> spread initialized at 71% +
+
+
+ + + + + + diff --git a/docs/butter.js b/docs/butter.js new file mode 100644 index 00000000..59237e01 --- /dev/null +++ b/docs/butter.js @@ -0,0 +1,40 @@ +(() => { + const spreadInput = document.getElementById("spread-input"); + const stateSelect = document.getElementById("butter-state"); + const butterButton = document.getElementById("butter-button"); + const spread = document.getElementById("butter-spread"); + const log = document.getElementById("butter-log"); + + if (!spreadInput || !stateSelect || !butterButton || !spread || !log) { + return; + } + + const butterCopy = { + spread: "spread applied: pipelines glide across warm operational toast", + churn: "churn complete: task lanes emulsified into cooperative momentum", + melt: "melt engaged: blockers softened into actionable droplets", + clarify: "clarify complete: noisy solids removed from the execution path", + }; + + function renderButter() { + const coverage = Number(spreadInput.value); + const state = stateSelect.value; + spread.style.width = `${coverage}%`; + spread.dataset.coverage = String(coverage); + log.textContent = `butter> ${butterCopy[state]} at ${coverage}% coverage`; + } + + spreadInput.addEventListener("input", renderButter); + stateSelect.addEventListener("change", renderButter); + butterButton.addEventListener("click", renderButter); + + renderButter(); + + window.AgentPipeButter = { + butterCopy, + renderButter, + get coverage() { + return Number(spread.dataset.coverage || 0); + }, + }; +})(); diff --git a/docs/index.html b/docs/index.html index a4d790a5..286275fd 100644 --- a/docs/index.html +++ b/docs/index.html @@ -20,6 +20,7 @@ diff --git a/package.json b/package.json index 92ddf23e..6d10cbcd 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,7 @@ { + "scripts": { + "test:butter": "node tests/butter-page.test.mjs" + }, "dependencies": { "@11ty/eleventy": "^3.1.6" } diff --git a/tests/butter-page.test.mjs b/tests/butter-page.test.mjs new file mode 100644 index 00000000..62fcbff1 --- /dev/null +++ b/tests/butter-page.test.mjs @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = join(dirname(fileURLToPath(import.meta.url)), '..'); +const html = readFileSync(join(root, 'docs', 'butter.html'), 'utf8'); +const js = readFileSync(join(root, 'docs', 'butter.js'), 'utf8'); +const index = readFileSync(join(root, 'docs', 'index.html'), 'utf8'); + +assert.match(html, /AgentPipe Butter Mode<\/title>/, 'butter page should have a specific title'); +assert.match(html, /Issue #1878: butter/, 'butter page should reference the bounty issue'); +assert.match(html, /id="spread-input"/, 'butter page should expose a spread range control'); +assert.match(html, /id="butter-state"/, 'butter page should expose state selection'); +assert.match(html, /id="butter-button"/, 'butter page should expose an apply button'); +assert.match(html, /id="butter-spread"/, 'butter page should expose a visual meter'); +assert.match(html, /id="butter-log"/, 'butter page should expose live output'); +assert.match(html, /butter\.js/, 'butter page should load its script'); +assert.match(html, /spread|churn|melt|clarify/, 'butter page should include the butter lifecycle states'); +assert.match(index, /href="butter\.html"/, 'home nav should link to butter page'); + +for (const state of ['spread', 'churn', 'melt', 'clarify']) { + assert.match(js, new RegExp(`${state}:`), `butter JS should define copy for ${state}`); +} +assert.match(js, /window\.AgentPipeButter/, 'butter JS should expose a small smoke-test API'); +assert.match(js, /dataset\.coverage/, 'butter JS should store rendered coverage for smoke testing'); + +console.log('butter-page checks passed');