[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1

Closed
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137
Closed

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Summary

Fixescluesmith#1137

The gitea forge preset was authored against the Gitea REST API JSON shape
but invoked the tea CLI, whose tea <entity> list/view output is a
flattened, --fields-limited view (and in some cases the referenced
flag/field/subcommand doesn't exist). Every read concept either errored or
emitted a shape that didn't match forge-contracts.ts.

Root Cause

tea exposes two divergent JSON surfaces:

  1. tea <entity> list/view --output json — flattened/limited (head/base are
    strings, no body/description, merged state synthesized, whoami has no JSON).
  2. tea api <endpoint> — a raw passthrough returning the canonical Gitea REST
    shape that codev's jq normalizers + forge-contracts.ts already assume.

The scripts read from (1); the contracts expect (2).

Fix

Route the read concepts through tea api:

ConceptChange
user-identitytea api user | jq .login (tea whoami has no --output json)
pr-viewtea api repos/<repo>/pulls/NPrViewResult
pr-listtea api repos/<repo>/pulls?state=openPrListItem[]; now also populates real reviewRequests/isDraft/body
pr-existstea api repos/<repo>/pulls?state=all with nested .head.ref + .merged bool
issue-viewtea api repos/<repo>/issues/N+ a second call for the comments ARRAY (Gitea's issue object reports comments as an int count, which would crash consumers' .comments.filter(...))
recently-mergedtea api repos/<repo>/pulls?state=closed, filter .merged, use real .merged_at
issue-commenttea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment (unlike tea <entity>,
which auto-detects it from the local git remote), and most concepts are invoked
withoutCODEV_REPO set (e.g. pr-exists receives only CODEV_BRANCH_NAME),
so each api-based script derives owner/repo from the origin remote, honoring
CODEV_REPO when present.

Left issue-search untouched: it isn't in the issue's broken list, and its only
difference from the working issue-list is an unverified body field — changing
working/unverified code on assumption would violate minimal-change.

Test Plan

Note on the full test suite

The full npm test run shows 9 failures in unrelated files
(team-update collectEvents ×7, team-github, team-cli) — all 5s-timeout I/O
that hangs in this resource-starved worktree/environment. Confirmed failing on
the clean base without these changes, so they are pre-existing and
environmental (they pass in CI). This PR touches none of those files.

🤖 Generated with Claude Code

pseudoseedand others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:
- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view: `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list: `tea api repos/<repo>/pulls?state=open` → PrListItem[]
(now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
ARRAY (Gitea's issue object reports `comments` as an int count,
which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)
`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.
Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
OwnerAuthor

Superseded by the upstream bugfix PR cluesmith#1146 (same branch, targeting the codev repo). Closing this fork-internal duplicate.

pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…cker, perf bound, CJS interop)
Iteration 2 of Phase 2, addressing the iter-1 3-way review (Gemini + Claude APPROVE, Codex REQUEST_CHANGES):
- Codex #1 (missing claude-picker fixture): add a synthesized claude `/model` picker fixture (claude-picker.busy.txt) whose highlighted row starts with the same ❯ glyph as the composer marker, with normal-intensity model names. Pins that a picker's selection-cursor + list classifies busy via the user-text path, never a false-clean; mirrors the real codex-picker capture (`› 1. …`). Wired into the required-states assertion; suite now 23/23. Documented as synthesized in the fixtures README (sandbox claude is the ez-cli shim, same reason as claude-idle).
- Codex #2 (perf assertion too loose): replace the single cold-run < 500ms with warm-up + best-of-5 min < 75ms. The min strips JIT/GC/scheduling noise (42.7ms cold vs 14.5ms native steady-state here), so it validates the spec's ≤~50ms seed-cap budget (measured best-of-5 = 19.2ms) instead of flaking. 5x tighter than before; 75ms is the CI-noise ceiling, not a near-budget claim (the logged value is the evidence).
- Bonus latent production bug, found while grounding the perf measurement against the compiled dist under native node: @xterm/headless resolves to its CommonJS entry (no exports map / type:module) with non-analyzable named exports, so `import { Terminal }` throws "Named export 'Terminal' not found" under native-node ESM — how the compiled bins run in production. Masked by vitest (vite interop) and dormant until Phase 4 wires the gate. Switch to the default-import form (codebase convention, cf. `import Database from 'better-sqlite3'`) plus a type-only alias for the one type-position use.
Refs cluesmith#1313.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…-ceiling+memo
All three reviewers (Gemini/Codex/Claude) returned REQUEST_CHANGES; all agreed the
over-ceiling removal itself is correct and shippable. Fixes:
- Memo stale-verdict across a same-agentKey PTY respawn / RingBuffer.clear() (all
three, HIGH): the ringToken (currentSeq:partialBytes:...) is only unique WITHIN one
monotonic ring, so a token can alias across session instances. CachedVerdict now
binds the live session instance too (hit = cached.session===session && token);
getSession(tid) is stable per live terminal, so it hits across ticks and misses
after a respawn. Test added.
- CPU regression: the memo does NOT help the expensive case (Claude #1; Codex OOM).
A BUSY held ring repaints every tick -> token changes every tick -> the memo always
misses exactly when the ring is largest (~230ms/tick/agent, await-serial). Added a
cost-aware backstop backoff: after a big (> BIG_RING_UNITS=4M) not-clean render, the
backstop skips re-classifying that agent for an exponential span (<=8 ticks). NEVER a
hold - scheduleDrain still classifies fresh the instant the line clears, so delivery
latency is unaffected. Test added.
- OOM doc corrected (Codex + Claude): the residual is a possible Tower OOM/crash
(unbounded allocation), not merely an event-loop stall (xterm chunks + yields). No
holding cap added (it would just reintroduce the outage); robust fix = off-thread
classify / cluesmith#1047 persistent xterm, out of scope.
- Interrupt Ctrl+C was OUTSIDE the submitToSession lock (all three): a concurrent
submission's Ctrl+C could kill another composer / run in the 100ms gap. Now the
Ctrl+C + settle (via writeMessageToSession delayOffset) + write are one atomic locked
section. Corrected the overstated anti-fusion claim (serializes interrupt-vs-escape
only, not vs a concurrent mailbox delivery).
- spec-1280 T16 predicate (all three): my manifest-dir-touch scoping silently skipped
the forgot-the-manifest-entirely case + had a Windows path.sep bug (always skipped).
Adopted Claude's portable predicate (/1280/ branch OR touches codev/projects/1280).
- stop() now clears verdictMemo/notCleanStreak/scheduledDrains/classifyBackoff
(Codex + Claude). cron test asserts target via objectContaining (Claude).
Deferred/flagged for the architect: off-thread/memory-bounded classify (cluesmith#1047); the
mailbox write edge taking the per-terminal lock to kill interrupt-vs-delivery fusion;
the interrupt-throw -> re-deliver duplicate (minor).
Full unit suite: 4261 pass / 48 skip / 0 fail. tsc clean.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

1 participant

@pseudoseed
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1

Closed
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137
Closed

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Summary

Fixescluesmith#1137

The gitea forge preset was authored against the Gitea REST API JSON shape
but invoked the tea CLI, whose tea <entity> list/view output is a
flattened, --fields-limited view (and in some cases the referenced
flag/field/subcommand doesn't exist). Every read concept either errored or
emitted a shape that didn't match forge-contracts.ts.

Root Cause

tea exposes two divergent JSON surfaces:

  1. tea <entity> list/view --output json — flattened/limited (head/base are
    strings, no body/description, merged state synthesized, whoami has no JSON).
  2. tea api <endpoint> — a raw passthrough returning the canonical Gitea REST
    shape that codev's jq normalizers + forge-contracts.ts already assume.

The scripts read from (1); the contracts expect (2).

Fix

Route the read concepts through tea api:

ConceptChange
user-identitytea api user | jq .login (tea whoami has no --output json)
pr-viewtea api repos/<repo>/pulls/NPrViewResult
pr-listtea api repos/<repo>/pulls?state=openPrListItem[]; now also populates real reviewRequests/isDraft/body
pr-existstea api repos/<repo>/pulls?state=all with nested .head.ref + .merged bool
issue-viewtea api repos/<repo>/issues/N+ a second call for the comments ARRAY (Gitea's issue object reports comments as an int count, which would crash consumers' .comments.filter(...))
recently-mergedtea api repos/<repo>/pulls?state=closed, filter .merged, use real .merged_at
issue-commenttea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment (unlike tea <entity>,
which auto-detects it from the local git remote), and most concepts are invoked
withoutCODEV_REPO set (e.g. pr-exists receives only CODEV_BRANCH_NAME),
so each api-based script derives owner/repo from the origin remote, honoring
CODEV_REPO when present.

Left issue-search untouched: it isn't in the issue's broken list, and its only
difference from the working issue-list is an unverified body field — changing
working/unverified code on assumption would violate minimal-change.

Test Plan

Note on the full test suite

The full npm test run shows 9 failures in unrelated files
(team-update collectEvents ×7, team-github, team-cli) — all 5s-timeout I/O
that hangs in this resource-starved worktree/environment. Confirmed failing on
the clean base without these changes, so they are pre-existing and
environmental (they pass in CI). This PR touches none of those files.

🤖 Generated with Claude Code

pseudoseedand others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:
- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view: `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list: `tea api repos/<repo>/pulls?state=open` → PrListItem[]
(now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
ARRAY (Gitea's issue object reports `comments` as an int count,
which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)
`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.
Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
OwnerAuthor

Superseded by the upstream bugfix PR cluesmith#1146 (same branch, targeting the codev repo). Closing this fork-internal duplicate.

pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…cker, perf bound, CJS interop)
Iteration 2 of Phase 2, addressing the iter-1 3-way review (Gemini + Claude APPROVE, Codex REQUEST_CHANGES):
- Codex #1 (missing claude-picker fixture): add a synthesized claude `/model` picker fixture (claude-picker.busy.txt) whose highlighted row starts with the same ❯ glyph as the composer marker, with normal-intensity model names. Pins that a picker's selection-cursor + list classifies busy via the user-text path, never a false-clean; mirrors the real codex-picker capture (`› 1. …`). Wired into the required-states assertion; suite now 23/23. Documented as synthesized in the fixtures README (sandbox claude is the ez-cli shim, same reason as claude-idle).
- Codex #2 (perf assertion too loose): replace the single cold-run < 500ms with warm-up + best-of-5 min < 75ms. The min strips JIT/GC/scheduling noise (42.7ms cold vs 14.5ms native steady-state here), so it validates the spec's ≤~50ms seed-cap budget (measured best-of-5 = 19.2ms) instead of flaking. 5x tighter than before; 75ms is the CI-noise ceiling, not a near-budget claim (the logged value is the evidence).
- Bonus latent production bug, found while grounding the perf measurement against the compiled dist under native node: @xterm/headless resolves to its CommonJS entry (no exports map / type:module) with non-analyzable named exports, so `import { Terminal }` throws "Named export 'Terminal' not found" under native-node ESM — how the compiled bins run in production. Masked by vitest (vite interop) and dormant until Phase 4 wires the gate. Switch to the default-import form (codebase convention, cf. `import Database from 'better-sqlite3'`) plus a type-only alias for the one type-position use.
Refs cluesmith#1313.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…-ceiling+memo
All three reviewers (Gemini/Codex/Claude) returned REQUEST_CHANGES; all agreed the
over-ceiling removal itself is correct and shippable. Fixes:
- Memo stale-verdict across a same-agentKey PTY respawn / RingBuffer.clear() (all
three, HIGH): the ringToken (currentSeq:partialBytes:...) is only unique WITHIN one
monotonic ring, so a token can alias across session instances. CachedVerdict now
binds the live session instance too (hit = cached.session===session && token);
getSession(tid) is stable per live terminal, so it hits across ticks and misses
after a respawn. Test added.
- CPU regression: the memo does NOT help the expensive case (Claude #1; Codex OOM).
A BUSY held ring repaints every tick -> token changes every tick -> the memo always
misses exactly when the ring is largest (~230ms/tick/agent, await-serial). Added a
cost-aware backstop backoff: after a big (> BIG_RING_UNITS=4M) not-clean render, the
backstop skips re-classifying that agent for an exponential span (<=8 ticks). NEVER a
hold - scheduleDrain still classifies fresh the instant the line clears, so delivery
latency is unaffected. Test added.
- OOM doc corrected (Codex + Claude): the residual is a possible Tower OOM/crash
(unbounded allocation), not merely an event-loop stall (xterm chunks + yields). No
holding cap added (it would just reintroduce the outage); robust fix = off-thread
classify / cluesmith#1047 persistent xterm, out of scope.
- Interrupt Ctrl+C was OUTSIDE the submitToSession lock (all three): a concurrent
submission's Ctrl+C could kill another composer / run in the 100ms gap. Now the
Ctrl+C + settle (via writeMessageToSession delayOffset) + write are one atomic locked
section. Corrected the overstated anti-fusion claim (serializes interrupt-vs-escape
only, not vs a concurrent mailbox delivery).
- spec-1280 T16 predicate (all three): my manifest-dir-touch scoping silently skipped
the forgot-the-manifest-entirely case + had a Windows path.sep bug (always skipped).
Adopted Claude's portable predicate (/1280/ branch OR touches codev/projects/1280).
- stop() now clears verdictMemo/notCleanStreak/scheduledDrains/classifyBackoff
(Codex + Claude). cron test asserts target via objectContaining (Claude).
Deferred/flagged for the architect: off-thread/memory-bounded classify (cluesmith#1047); the
mailbox write edge taking the per-terminal lock to kill interrupt-vs-delivery fusion;
the interrupt-throw -> re-deliver duplicate (minor).
Full unit suite: 4261 pass / 48 skip / 0 fail. tsc clean.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

1 participant

@pseudoseed
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1

Closed
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137
Closed

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Summary

Fixescluesmith#1137

The gitea forge preset was authored against the Gitea REST API JSON shape
but invoked the tea CLI, whose tea <entity> list/view output is a
flattened, --fields-limited view (and in some cases the referenced
flag/field/subcommand doesn't exist). Every read concept either errored or
emitted a shape that didn't match forge-contracts.ts.

Root Cause

tea exposes two divergent JSON surfaces:

  1. tea <entity> list/view --output json — flattened/limited (head/base are
    strings, no body/description, merged state synthesized, whoami has no JSON).
  2. tea api <endpoint> — a raw passthrough returning the canonical Gitea REST
    shape that codev's jq normalizers + forge-contracts.ts already assume.

The scripts read from (1); the contracts expect (2).

Fix

Route the read concepts through tea api:

ConceptChange
user-identitytea api user | jq .login (tea whoami has no --output json)
pr-viewtea api repos/<repo>/pulls/NPrViewResult
pr-listtea api repos/<repo>/pulls?state=openPrListItem[]; now also populates real reviewRequests/isDraft/body
pr-existstea api repos/<repo>/pulls?state=all with nested .head.ref + .merged bool
issue-viewtea api repos/<repo>/issues/N+ a second call for the comments ARRAY (Gitea's issue object reports comments as an int count, which would crash consumers' .comments.filter(...))
recently-mergedtea api repos/<repo>/pulls?state=closed, filter .merged, use real .merged_at
issue-commenttea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment (unlike tea <entity>,
which auto-detects it from the local git remote), and most concepts are invoked
withoutCODEV_REPO set (e.g. pr-exists receives only CODEV_BRANCH_NAME),
so each api-based script derives owner/repo from the origin remote, honoring
CODEV_REPO when present.

Left issue-search untouched: it isn't in the issue's broken list, and its only
difference from the working issue-list is an unverified body field — changing
working/unverified code on assumption would violate minimal-change.

Test Plan

Note on the full test suite

The full npm test run shows 9 failures in unrelated files
(team-update collectEvents ×7, team-github, team-cli) — all 5s-timeout I/O
that hangs in this resource-starved worktree/environment. Confirmed failing on
the clean base without these changes, so they are pre-existing and
environmental (they pass in CI). This PR touches none of those files.

🤖 Generated with Claude Code

pseudoseedand others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:
- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view: `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list: `tea api repos/<repo>/pulls?state=open` → PrListItem[]
(now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
ARRAY (Gitea's issue object reports `comments` as an int count,
which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)
`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.
Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
OwnerAuthor

Superseded by the upstream bugfix PR cluesmith#1146 (same branch, targeting the codev repo). Closing this fork-internal duplicate.

pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…cker, perf bound, CJS interop)
Iteration 2 of Phase 2, addressing the iter-1 3-way review (Gemini + Claude APPROVE, Codex REQUEST_CHANGES):
- Codex #1 (missing claude-picker fixture): add a synthesized claude `/model` picker fixture (claude-picker.busy.txt) whose highlighted row starts with the same ❯ glyph as the composer marker, with normal-intensity model names. Pins that a picker's selection-cursor + list classifies busy via the user-text path, never a false-clean; mirrors the real codex-picker capture (`› 1. …`). Wired into the required-states assertion; suite now 23/23. Documented as synthesized in the fixtures README (sandbox claude is the ez-cli shim, same reason as claude-idle).
- Codex #2 (perf assertion too loose): replace the single cold-run < 500ms with warm-up + best-of-5 min < 75ms. The min strips JIT/GC/scheduling noise (42.7ms cold vs 14.5ms native steady-state here), so it validates the spec's ≤~50ms seed-cap budget (measured best-of-5 = 19.2ms) instead of flaking. 5x tighter than before; 75ms is the CI-noise ceiling, not a near-budget claim (the logged value is the evidence).
- Bonus latent production bug, found while grounding the perf measurement against the compiled dist under native node: @xterm/headless resolves to its CommonJS entry (no exports map / type:module) with non-analyzable named exports, so `import { Terminal }` throws "Named export 'Terminal' not found" under native-node ESM — how the compiled bins run in production. Masked by vitest (vite interop) and dormant until Phase 4 wires the gate. Switch to the default-import form (codebase convention, cf. `import Database from 'better-sqlite3'`) plus a type-only alias for the one type-position use.
Refs cluesmith#1313.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…-ceiling+memo
All three reviewers (Gemini/Codex/Claude) returned REQUEST_CHANGES; all agreed the
over-ceiling removal itself is correct and shippable. Fixes:
- Memo stale-verdict across a same-agentKey PTY respawn / RingBuffer.clear() (all
three, HIGH): the ringToken (currentSeq:partialBytes:...) is only unique WITHIN one
monotonic ring, so a token can alias across session instances. CachedVerdict now
binds the live session instance too (hit = cached.session===session && token);
getSession(tid) is stable per live terminal, so it hits across ticks and misses
after a respawn. Test added.
- CPU regression: the memo does NOT help the expensive case (Claude #1; Codex OOM).
A BUSY held ring repaints every tick -> token changes every tick -> the memo always
misses exactly when the ring is largest (~230ms/tick/agent, await-serial). Added a
cost-aware backstop backoff: after a big (> BIG_RING_UNITS=4M) not-clean render, the
backstop skips re-classifying that agent for an exponential span (<=8 ticks). NEVER a
hold - scheduleDrain still classifies fresh the instant the line clears, so delivery
latency is unaffected. Test added.
- OOM doc corrected (Codex + Claude): the residual is a possible Tower OOM/crash
(unbounded allocation), not merely an event-loop stall (xterm chunks + yields). No
holding cap added (it would just reintroduce the outage); robust fix = off-thread
classify / cluesmith#1047 persistent xterm, out of scope.
- Interrupt Ctrl+C was OUTSIDE the submitToSession lock (all three): a concurrent
submission's Ctrl+C could kill another composer / run in the 100ms gap. Now the
Ctrl+C + settle (via writeMessageToSession delayOffset) + write are one atomic locked
section. Corrected the overstated anti-fusion claim (serializes interrupt-vs-escape
only, not vs a concurrent mailbox delivery).
- spec-1280 T16 predicate (all three): my manifest-dir-touch scoping silently skipped
the forgot-the-manifest-entirely case + had a Windows path.sep bug (always skipped).
Adopted Claude's portable predicate (/1280/ branch OR touches codev/projects/1280).
- stop() now clears verdictMemo/notCleanStreak/scheduledDrains/classifyBackoff
(Codex + Claude). cron test asserts target via objectContaining (Claude).
Deferred/flagged for the architect: off-thread/memory-bounded classify (cluesmith#1047); the
mailbox write edge taking the per-terminal lock to kill interrupt-vs-delivery fusion;
the interrupt-throw -> re-deliver duplicate (minor).
Full unit suite: 4261 pass / 48 skip / 0 fail. tsc clean.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

1 participant

@pseudoseed
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1

Closed
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137
Closed

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Summary

Fixescluesmith#1137

The gitea forge preset was authored against the Gitea REST API JSON shape
but invoked the tea CLI, whose tea <entity> list/view output is a
flattened, --fields-limited view (and in some cases the referenced
flag/field/subcommand doesn't exist). Every read concept either errored or
emitted a shape that didn't match forge-contracts.ts.

Root Cause

tea exposes two divergent JSON surfaces:

  1. tea <entity> list/view --output json — flattened/limited (head/base are
    strings, no body/description, merged state synthesized, whoami has no JSON).
  2. tea api <endpoint> — a raw passthrough returning the canonical Gitea REST
    shape that codev's jq normalizers + forge-contracts.ts already assume.

The scripts read from (1); the contracts expect (2).

Fix

Route the read concepts through tea api:

ConceptChange
user-identitytea api user | jq .login (tea whoami has no --output json)
pr-viewtea api repos/<repo>/pulls/NPrViewResult
pr-listtea api repos/<repo>/pulls?state=openPrListItem[]; now also populates real reviewRequests/isDraft/body
pr-existstea api repos/<repo>/pulls?state=all with nested .head.ref + .merged bool
issue-viewtea api repos/<repo>/issues/N+ a second call for the comments ARRAY (Gitea's issue object reports comments as an int count, which would crash consumers' .comments.filter(...))
recently-mergedtea api repos/<repo>/pulls?state=closed, filter .merged, use real .merged_at
issue-commenttea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment (unlike tea <entity>,
which auto-detects it from the local git remote), and most concepts are invoked
withoutCODEV_REPO set (e.g. pr-exists receives only CODEV_BRANCH_NAME),
so each api-based script derives owner/repo from the origin remote, honoring
CODEV_REPO when present.

Left issue-search untouched: it isn't in the issue's broken list, and its only
difference from the working issue-list is an unverified body field — changing
working/unverified code on assumption would violate minimal-change.

Test Plan

Note on the full test suite

The full npm test run shows 9 failures in unrelated files
(team-update collectEvents ×7, team-github, team-cli) — all 5s-timeout I/O
that hangs in this resource-starved worktree/environment. Confirmed failing on
the clean base without these changes, so they are pre-existing and
environmental (they pass in CI). This PR touches none of those files.

🤖 Generated with Claude Code

pseudoseedand others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:
- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view: `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list: `tea api repos/<repo>/pulls?state=open` → PrListItem[]
(now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
ARRAY (Gitea's issue object reports `comments` as an int count,
which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)
`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.
Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
OwnerAuthor

Superseded by the upstream bugfix PR cluesmith#1146 (same branch, targeting the codev repo). Closing this fork-internal duplicate.

pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…cker, perf bound, CJS interop)
Iteration 2 of Phase 2, addressing the iter-1 3-way review (Gemini + Claude APPROVE, Codex REQUEST_CHANGES):
- Codex #1 (missing claude-picker fixture): add a synthesized claude `/model` picker fixture (claude-picker.busy.txt) whose highlighted row starts with the same ❯ glyph as the composer marker, with normal-intensity model names. Pins that a picker's selection-cursor + list classifies busy via the user-text path, never a false-clean; mirrors the real codex-picker capture (`› 1. …`). Wired into the required-states assertion; suite now 23/23. Documented as synthesized in the fixtures README (sandbox claude is the ez-cli shim, same reason as claude-idle).
- Codex #2 (perf assertion too loose): replace the single cold-run < 500ms with warm-up + best-of-5 min < 75ms. The min strips JIT/GC/scheduling noise (42.7ms cold vs 14.5ms native steady-state here), so it validates the spec's ≤~50ms seed-cap budget (measured best-of-5 = 19.2ms) instead of flaking. 5x tighter than before; 75ms is the CI-noise ceiling, not a near-budget claim (the logged value is the evidence).
- Bonus latent production bug, found while grounding the perf measurement against the compiled dist under native node: @xterm/headless resolves to its CommonJS entry (no exports map / type:module) with non-analyzable named exports, so `import { Terminal }` throws "Named export 'Terminal' not found" under native-node ESM — how the compiled bins run in production. Masked by vitest (vite interop) and dormant until Phase 4 wires the gate. Switch to the default-import form (codebase convention, cf. `import Database from 'better-sqlite3'`) plus a type-only alias for the one type-position use.
Refs cluesmith#1313.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…-ceiling+memo
All three reviewers (Gemini/Codex/Claude) returned REQUEST_CHANGES; all agreed the
over-ceiling removal itself is correct and shippable. Fixes:
- Memo stale-verdict across a same-agentKey PTY respawn / RingBuffer.clear() (all
three, HIGH): the ringToken (currentSeq:partialBytes:...) is only unique WITHIN one
monotonic ring, so a token can alias across session instances. CachedVerdict now
binds the live session instance too (hit = cached.session===session && token);
getSession(tid) is stable per live terminal, so it hits across ticks and misses
after a respawn. Test added.
- CPU regression: the memo does NOT help the expensive case (Claude #1; Codex OOM).
A BUSY held ring repaints every tick -> token changes every tick -> the memo always
misses exactly when the ring is largest (~230ms/tick/agent, await-serial). Added a
cost-aware backstop backoff: after a big (> BIG_RING_UNITS=4M) not-clean render, the
backstop skips re-classifying that agent for an exponential span (<=8 ticks). NEVER a
hold - scheduleDrain still classifies fresh the instant the line clears, so delivery
latency is unaffected. Test added.
- OOM doc corrected (Codex + Claude): the residual is a possible Tower OOM/crash
(unbounded allocation), not merely an event-loop stall (xterm chunks + yields). No
holding cap added (it would just reintroduce the outage); robust fix = off-thread
classify / cluesmith#1047 persistent xterm, out of scope.
- Interrupt Ctrl+C was OUTSIDE the submitToSession lock (all three): a concurrent
submission's Ctrl+C could kill another composer / run in the 100ms gap. Now the
Ctrl+C + settle (via writeMessageToSession delayOffset) + write are one atomic locked
section. Corrected the overstated anti-fusion claim (serializes interrupt-vs-escape
only, not vs a concurrent mailbox delivery).
- spec-1280 T16 predicate (all three): my manifest-dir-touch scoping silently skipped
the forgot-the-manifest-entirely case + had a Windows path.sep bug (always skipped).
Adopted Claude's portable predicate (/1280/ branch OR touches codev/projects/1280).
- stop() now clears verdictMemo/notCleanStreak/scheduledDrains/classifyBackoff
(Codex + Claude). cron test asserts target via objectContaining (Claude).
Deferred/flagged for the architect: off-thread/memory-bounded classify (cluesmith#1047); the
mailbox write edge taking the per-terminal lock to kill interrupt-vs-delivery fusion;
the interrupt-throw -> re-deliver duplicate (minor).
Full unit suite: 4261 pass / 48 skip / 0 fail. tsc clean.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

1 participant

@pseudoseed
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1

Closed
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137
Closed

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Summary

Fixescluesmith#1137

The gitea forge preset was authored against the Gitea REST API JSON shape
but invoked the tea CLI, whose tea <entity> list/view output is a
flattened, --fields-limited view (and in some cases the referenced
flag/field/subcommand doesn't exist). Every read concept either errored or
emitted a shape that didn't match forge-contracts.ts.

Root Cause

tea exposes two divergent JSON surfaces:

  1. tea <entity> list/view --output json — flattened/limited (head/base are
    strings, no body/description, merged state synthesized, whoami has no JSON).
  2. tea api <endpoint> — a raw passthrough returning the canonical Gitea REST
    shape that codev's jq normalizers + forge-contracts.ts already assume.

The scripts read from (1); the contracts expect (2).

Fix

Route the read concepts through tea api:

ConceptChange
user-identitytea api user | jq .login (tea whoami has no --output json)
pr-viewtea api repos/<repo>/pulls/NPrViewResult
pr-listtea api repos/<repo>/pulls?state=openPrListItem[]; now also populates real reviewRequests/isDraft/body
pr-existstea api repos/<repo>/pulls?state=all with nested .head.ref + .merged bool
issue-viewtea api repos/<repo>/issues/N+ a second call for the comments ARRAY (Gitea's issue object reports comments as an int count, which would crash consumers' .comments.filter(...))
recently-mergedtea api repos/<repo>/pulls?state=closed, filter .merged, use real .merged_at
issue-commenttea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment (unlike tea <entity>,
which auto-detects it from the local git remote), and most concepts are invoked
withoutCODEV_REPO set (e.g. pr-exists receives only CODEV_BRANCH_NAME),
so each api-based script derives owner/repo from the origin remote, honoring
CODEV_REPO when present.

Left issue-search untouched: it isn't in the issue's broken list, and its only
difference from the working issue-list is an unverified body field — changing
working/unverified code on assumption would violate minimal-change.

Test Plan

Note on the full test suite

The full npm test run shows 9 failures in unrelated files
(team-update collectEvents ×7, team-github, team-cli) — all 5s-timeout I/O
that hangs in this resource-starved worktree/environment. Confirmed failing on
the clean base without these changes, so they are pre-existing and
environmental (they pass in CI). This PR touches none of those files.

🤖 Generated with Claude Code

pseudoseedand others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:
- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view: `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list: `tea api repos/<repo>/pulls?state=open` → PrListItem[]
(now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
ARRAY (Gitea's issue object reports `comments` as an int count,
which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)
`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.
Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
OwnerAuthor

Superseded by the upstream bugfix PR cluesmith#1146 (same branch, targeting the codev repo). Closing this fork-internal duplicate.

pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…cker, perf bound, CJS interop)
Iteration 2 of Phase 2, addressing the iter-1 3-way review (Gemini + Claude APPROVE, Codex REQUEST_CHANGES):
- Codex #1 (missing claude-picker fixture): add a synthesized claude `/model` picker fixture (claude-picker.busy.txt) whose highlighted row starts with the same ❯ glyph as the composer marker, with normal-intensity model names. Pins that a picker's selection-cursor + list classifies busy via the user-text path, never a false-clean; mirrors the real codex-picker capture (`› 1. …`). Wired into the required-states assertion; suite now 23/23. Documented as synthesized in the fixtures README (sandbox claude is the ez-cli shim, same reason as claude-idle).
- Codex #2 (perf assertion too loose): replace the single cold-run < 500ms with warm-up + best-of-5 min < 75ms. The min strips JIT/GC/scheduling noise (42.7ms cold vs 14.5ms native steady-state here), so it validates the spec's ≤~50ms seed-cap budget (measured best-of-5 = 19.2ms) instead of flaking. 5x tighter than before; 75ms is the CI-noise ceiling, not a near-budget claim (the logged value is the evidence).
- Bonus latent production bug, found while grounding the perf measurement against the compiled dist under native node: @xterm/headless resolves to its CommonJS entry (no exports map / type:module) with non-analyzable named exports, so `import { Terminal }` throws "Named export 'Terminal' not found" under native-node ESM — how the compiled bins run in production. Masked by vitest (vite interop) and dormant until Phase 4 wires the gate. Switch to the default-import form (codebase convention, cf. `import Database from 'better-sqlite3'`) plus a type-only alias for the one type-position use.
Refs cluesmith#1313.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…-ceiling+memo
All three reviewers (Gemini/Codex/Claude) returned REQUEST_CHANGES; all agreed the
over-ceiling removal itself is correct and shippable. Fixes:
- Memo stale-verdict across a same-agentKey PTY respawn / RingBuffer.clear() (all
three, HIGH): the ringToken (currentSeq:partialBytes:...) is only unique WITHIN one
monotonic ring, so a token can alias across session instances. CachedVerdict now
binds the live session instance too (hit = cached.session===session && token);
getSession(tid) is stable per live terminal, so it hits across ticks and misses
after a respawn. Test added.
- CPU regression: the memo does NOT help the expensive case (Claude #1; Codex OOM).
A BUSY held ring repaints every tick -> token changes every tick -> the memo always
misses exactly when the ring is largest (~230ms/tick/agent, await-serial). Added a
cost-aware backstop backoff: after a big (> BIG_RING_UNITS=4M) not-clean render, the
backstop skips re-classifying that agent for an exponential span (<=8 ticks). NEVER a
hold - scheduleDrain still classifies fresh the instant the line clears, so delivery
latency is unaffected. Test added.
- OOM doc corrected (Codex + Claude): the residual is a possible Tower OOM/crash
(unbounded allocation), not merely an event-loop stall (xterm chunks + yields). No
holding cap added (it would just reintroduce the outage); robust fix = off-thread
classify / cluesmith#1047 persistent xterm, out of scope.
- Interrupt Ctrl+C was OUTSIDE the submitToSession lock (all three): a concurrent
submission's Ctrl+C could kill another composer / run in the 100ms gap. Now the
Ctrl+C + settle (via writeMessageToSession delayOffset) + write are one atomic locked
section. Corrected the overstated anti-fusion claim (serializes interrupt-vs-escape
only, not vs a concurrent mailbox delivery).
- spec-1280 T16 predicate (all three): my manifest-dir-touch scoping silently skipped
the forgot-the-manifest-entirely case + had a Windows path.sep bug (always skipped).
Adopted Claude's portable predicate (/1280/ branch OR touches codev/projects/1280).
- stop() now clears verdictMemo/notCleanStreak/scheduledDrains/classifyBackoff
(Codex + Claude). cron test asserts target via objectContaining (Claude).
Deferred/flagged for the architect: off-thread/memory-bounded classify (cluesmith#1047); the
mailbox write edge taking the per-terminal lock to kill interrupt-vs-delivery fusion;
the interrupt-throw -> re-deliver duplicate (minor).
Full unit suite: 4261 pass / 48 skip / 0 fail. tsc clean.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

1 participant

@pseudoseed
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1

Closed
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137
Closed

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Summary

Fixescluesmith#1137

The gitea forge preset was authored against the Gitea REST API JSON shape
but invoked the tea CLI, whose tea <entity> list/view output is a
flattened, --fields-limited view (and in some cases the referenced
flag/field/subcommand doesn't exist). Every read concept either errored or
emitted a shape that didn't match forge-contracts.ts.

Root Cause

tea exposes two divergent JSON surfaces:

  1. tea <entity> list/view --output json — flattened/limited (head/base are
    strings, no body/description, merged state synthesized, whoami has no JSON).
  2. tea api <endpoint> — a raw passthrough returning the canonical Gitea REST
    shape that codev's jq normalizers + forge-contracts.ts already assume.

The scripts read from (1); the contracts expect (2).

Fix

Route the read concepts through tea api:

ConceptChange
user-identitytea api user | jq .login (tea whoami has no --output json)
pr-viewtea api repos/<repo>/pulls/NPrViewResult
pr-listtea api repos/<repo>/pulls?state=openPrListItem[]; now also populates real reviewRequests/isDraft/body
pr-existstea api repos/<repo>/pulls?state=all with nested .head.ref + .merged bool
issue-viewtea api repos/<repo>/issues/N+ a second call for the comments ARRAY (Gitea's issue object reports comments as an int count, which would crash consumers' .comments.filter(...))
recently-mergedtea api repos/<repo>/pulls?state=closed, filter .merged, use real .merged_at
issue-commenttea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment (unlike tea <entity>,
which auto-detects it from the local git remote), and most concepts are invoked
withoutCODEV_REPO set (e.g. pr-exists receives only CODEV_BRANCH_NAME),
so each api-based script derives owner/repo from the origin remote, honoring
CODEV_REPO when present.

Left issue-search untouched: it isn't in the issue's broken list, and its only
difference from the working issue-list is an unverified body field — changing
working/unverified code on assumption would violate minimal-change.

Test Plan

Note on the full test suite

The full npm test run shows 9 failures in unrelated files
(team-update collectEvents ×7, team-github, team-cli) — all 5s-timeout I/O
that hangs in this resource-starved worktree/environment. Confirmed failing on
the clean base without these changes, so they are pre-existing and
environmental (they pass in CI). This PR touches none of those files.

🤖 Generated with Claude Code

pseudoseedand others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:
- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view: `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list: `tea api repos/<repo>/pulls?state=open` → PrListItem[]
(now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
ARRAY (Gitea's issue object reports `comments` as an int count,
which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)
`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.
Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
OwnerAuthor

Superseded by the upstream bugfix PR cluesmith#1146 (same branch, targeting the codev repo). Closing this fork-internal duplicate.

pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…cker, perf bound, CJS interop)
Iteration 2 of Phase 2, addressing the iter-1 3-way review (Gemini + Claude APPROVE, Codex REQUEST_CHANGES):
- Codex #1 (missing claude-picker fixture): add a synthesized claude `/model` picker fixture (claude-picker.busy.txt) whose highlighted row starts with the same ❯ glyph as the composer marker, with normal-intensity model names. Pins that a picker's selection-cursor + list classifies busy via the user-text path, never a false-clean; mirrors the real codex-picker capture (`› 1. …`). Wired into the required-states assertion; suite now 23/23. Documented as synthesized in the fixtures README (sandbox claude is the ez-cli shim, same reason as claude-idle).
- Codex #2 (perf assertion too loose): replace the single cold-run < 500ms with warm-up + best-of-5 min < 75ms. The min strips JIT/GC/scheduling noise (42.7ms cold vs 14.5ms native steady-state here), so it validates the spec's ≤~50ms seed-cap budget (measured best-of-5 = 19.2ms) instead of flaking. 5x tighter than before; 75ms is the CI-noise ceiling, not a near-budget claim (the logged value is the evidence).
- Bonus latent production bug, found while grounding the perf measurement against the compiled dist under native node: @xterm/headless resolves to its CommonJS entry (no exports map / type:module) with non-analyzable named exports, so `import { Terminal }` throws "Named export 'Terminal' not found" under native-node ESM — how the compiled bins run in production. Masked by vitest (vite interop) and dormant until Phase 4 wires the gate. Switch to the default-import form (codebase convention, cf. `import Database from 'better-sqlite3'`) plus a type-only alias for the one type-position use.
Refs cluesmith#1313.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…-ceiling+memo
All three reviewers (Gemini/Codex/Claude) returned REQUEST_CHANGES; all agreed the
over-ceiling removal itself is correct and shippable. Fixes:
- Memo stale-verdict across a same-agentKey PTY respawn / RingBuffer.clear() (all
three, HIGH): the ringToken (currentSeq:partialBytes:...) is only unique WITHIN one
monotonic ring, so a token can alias across session instances. CachedVerdict now
binds the live session instance too (hit = cached.session===session && token);
getSession(tid) is stable per live terminal, so it hits across ticks and misses
after a respawn. Test added.
- CPU regression: the memo does NOT help the expensive case (Claude #1; Codex OOM).
A BUSY held ring repaints every tick -> token changes every tick -> the memo always
misses exactly when the ring is largest (~230ms/tick/agent, await-serial). Added a
cost-aware backstop backoff: after a big (> BIG_RING_UNITS=4M) not-clean render, the
backstop skips re-classifying that agent for an exponential span (<=8 ticks). NEVER a
hold - scheduleDrain still classifies fresh the instant the line clears, so delivery
latency is unaffected. Test added.
- OOM doc corrected (Codex + Claude): the residual is a possible Tower OOM/crash
(unbounded allocation), not merely an event-loop stall (xterm chunks + yields). No
holding cap added (it would just reintroduce the outage); robust fix = off-thread
classify / cluesmith#1047 persistent xterm, out of scope.
- Interrupt Ctrl+C was OUTSIDE the submitToSession lock (all three): a concurrent
submission's Ctrl+C could kill another composer / run in the 100ms gap. Now the
Ctrl+C + settle (via writeMessageToSession delayOffset) + write are one atomic locked
section. Corrected the overstated anti-fusion claim (serializes interrupt-vs-escape
only, not vs a concurrent mailbox delivery).
- spec-1280 T16 predicate (all three): my manifest-dir-touch scoping silently skipped
the forgot-the-manifest-entirely case + had a Windows path.sep bug (always skipped).
Adopted Claude's portable predicate (/1280/ branch OR touches codev/projects/1280).
- stop() now clears verdictMemo/notCleanStreak/scheduledDrains/classifyBackoff
(Codex + Claude). cron test asserts target via objectContaining (Claude).
Deferred/flagged for the architect: off-thread/memory-bounded classify (cluesmith#1047); the
mailbox write edge taking the per-terminal lock to kill interrupt-vs-delivery fusion;
the interrupt-throw -> re-deliver duplicate (minor).
Full unit suite: 4261 pass / 48 skip / 0 fail. tsc clean.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

1 participant

@pseudoseed
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1

Closed
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137
Closed

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Summary

Fixescluesmith#1137

The gitea forge preset was authored against the Gitea REST API JSON shape
but invoked the tea CLI, whose tea <entity> list/view output is a
flattened, --fields-limited view (and in some cases the referenced
flag/field/subcommand doesn't exist). Every read concept either errored or
emitted a shape that didn't match forge-contracts.ts.

Root Cause

tea exposes two divergent JSON surfaces:

  1. tea <entity> list/view --output json — flattened/limited (head/base are
    strings, no body/description, merged state synthesized, whoami has no JSON).
  2. tea api <endpoint> — a raw passthrough returning the canonical Gitea REST
    shape that codev's jq normalizers + forge-contracts.ts already assume.

The scripts read from (1); the contracts expect (2).

Fix

Route the read concepts through tea api:

ConceptChange
user-identitytea api user | jq .login (tea whoami has no --output json)
pr-viewtea api repos/<repo>/pulls/NPrViewResult
pr-listtea api repos/<repo>/pulls?state=openPrListItem[]; now also populates real reviewRequests/isDraft/body
pr-existstea api repos/<repo>/pulls?state=all with nested .head.ref + .merged bool
issue-viewtea api repos/<repo>/issues/N+ a second call for the comments ARRAY (Gitea's issue object reports comments as an int count, which would crash consumers' .comments.filter(...))
recently-mergedtea api repos/<repo>/pulls?state=closed, filter .merged, use real .merged_at
issue-commenttea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment (unlike tea <entity>,
which auto-detects it from the local git remote), and most concepts are invoked
withoutCODEV_REPO set (e.g. pr-exists receives only CODEV_BRANCH_NAME),
so each api-based script derives owner/repo from the origin remote, honoring
CODEV_REPO when present.

Left issue-search untouched: it isn't in the issue's broken list, and its only
difference from the working issue-list is an unverified body field — changing
working/unverified code on assumption would violate minimal-change.

Test Plan

Note on the full test suite

The full npm test run shows 9 failures in unrelated files
(team-update collectEvents ×7, team-github, team-cli) — all 5s-timeout I/O
that hangs in this resource-starved worktree/environment. Confirmed failing on
the clean base without these changes, so they are pre-existing and
environmental (they pass in CI). This PR touches none of those files.

🤖 Generated with Claude Code

pseudoseedand others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:
- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view: `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list: `tea api repos/<repo>/pulls?state=open` → PrListItem[]
(now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
ARRAY (Gitea's issue object reports `comments` as an int count,
which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)
`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.
Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
OwnerAuthor

Superseded by the upstream bugfix PR cluesmith#1146 (same branch, targeting the codev repo). Closing this fork-internal duplicate.

pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…cker, perf bound, CJS interop)
Iteration 2 of Phase 2, addressing the iter-1 3-way review (Gemini + Claude APPROVE, Codex REQUEST_CHANGES):
- Codex #1 (missing claude-picker fixture): add a synthesized claude `/model` picker fixture (claude-picker.busy.txt) whose highlighted row starts with the same ❯ glyph as the composer marker, with normal-intensity model names. Pins that a picker's selection-cursor + list classifies busy via the user-text path, never a false-clean; mirrors the real codex-picker capture (`› 1. …`). Wired into the required-states assertion; suite now 23/23. Documented as synthesized in the fixtures README (sandbox claude is the ez-cli shim, same reason as claude-idle).
- Codex #2 (perf assertion too loose): replace the single cold-run < 500ms with warm-up + best-of-5 min < 75ms. The min strips JIT/GC/scheduling noise (42.7ms cold vs 14.5ms native steady-state here), so it validates the spec's ≤~50ms seed-cap budget (measured best-of-5 = 19.2ms) instead of flaking. 5x tighter than before; 75ms is the CI-noise ceiling, not a near-budget claim (the logged value is the evidence).
- Bonus latent production bug, found while grounding the perf measurement against the compiled dist under native node: @xterm/headless resolves to its CommonJS entry (no exports map / type:module) with non-analyzable named exports, so `import { Terminal }` throws "Named export 'Terminal' not found" under native-node ESM — how the compiled bins run in production. Masked by vitest (vite interop) and dormant until Phase 4 wires the gate. Switch to the default-import form (codebase convention, cf. `import Database from 'better-sqlite3'`) plus a type-only alias for the one type-position use.
Refs cluesmith#1313.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…-ceiling+memo
All three reviewers (Gemini/Codex/Claude) returned REQUEST_CHANGES; all agreed the
over-ceiling removal itself is correct and shippable. Fixes:
- Memo stale-verdict across a same-agentKey PTY respawn / RingBuffer.clear() (all
three, HIGH): the ringToken (currentSeq:partialBytes:...) is only unique WITHIN one
monotonic ring, so a token can alias across session instances. CachedVerdict now
binds the live session instance too (hit = cached.session===session && token);
getSession(tid) is stable per live terminal, so it hits across ticks and misses
after a respawn. Test added.
- CPU regression: the memo does NOT help the expensive case (Claude #1; Codex OOM).
A BUSY held ring repaints every tick -> token changes every tick -> the memo always
misses exactly when the ring is largest (~230ms/tick/agent, await-serial). Added a
cost-aware backstop backoff: after a big (> BIG_RING_UNITS=4M) not-clean render, the
backstop skips re-classifying that agent for an exponential span (<=8 ticks). NEVER a
hold - scheduleDrain still classifies fresh the instant the line clears, so delivery
latency is unaffected. Test added.
- OOM doc corrected (Codex + Claude): the residual is a possible Tower OOM/crash
(unbounded allocation), not merely an event-loop stall (xterm chunks + yields). No
holding cap added (it would just reintroduce the outage); robust fix = off-thread
classify / cluesmith#1047 persistent xterm, out of scope.
- Interrupt Ctrl+C was OUTSIDE the submitToSession lock (all three): a concurrent
submission's Ctrl+C could kill another composer / run in the 100ms gap. Now the
Ctrl+C + settle (via writeMessageToSession delayOffset) + write are one atomic locked
section. Corrected the overstated anti-fusion claim (serializes interrupt-vs-escape
only, not vs a concurrent mailbox delivery).
- spec-1280 T16 predicate (all three): my manifest-dir-touch scoping silently skipped
the forgot-the-manifest-entirely case + had a Windows path.sep bug (always skipped).
Adopted Claude's portable predicate (/1280/ branch OR touches codev/projects/1280).
- stop() now clears verdictMemo/notCleanStreak/scheduledDrains/classifyBackoff
(Codex + Claude). cron test asserts target via objectContaining (Claude).
Deferred/flagged for the architect: off-thread/memory-bounded classify (cluesmith#1047); the
mailbox write edge taking the per-terminal lock to kill interrupt-vs-delivery fusion;
the interrupt-throw -> re-deliver duplicate (minor).
Full unit suite: 4261 pass / 48 skip / 0 fail. tsc clean.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

1 participant

@pseudoseed
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[Bugfix #1137] Fix gitea forge preset against the real tea CLI - #1

Closed
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137
Closed

[Bugfix #1137] Fix gitea forge preset against the real tea CLI#1
pseudoseed wants to merge 4 commits into
mainfrom
builder/bugfix-1137

Conversation

@pseudoseed

Copy link
Copy Markdown
Owner

Summary

Fixescluesmith#1137

The gitea forge preset was authored against the Gitea REST API JSON shape
but invoked the tea CLI, whose tea <entity> list/view output is a
flattened, --fields-limited view (and in some cases the referenced
flag/field/subcommand doesn't exist). Every read concept either errored or
emitted a shape that didn't match forge-contracts.ts.

Root Cause

tea exposes two divergent JSON surfaces:

  1. tea <entity> list/view --output json — flattened/limited (head/base are
    strings, no body/description, merged state synthesized, whoami has no JSON).
  2. tea api <endpoint> — a raw passthrough returning the canonical Gitea REST
    shape that codev's jq normalizers + forge-contracts.ts already assume.

The scripts read from (1); the contracts expect (2).

Fix

Route the read concepts through tea api:

ConceptChange
user-identitytea api user | jq .login (tea whoami has no --output json)
pr-viewtea api repos/<repo>/pulls/NPrViewResult
pr-listtea api repos/<repo>/pulls?state=openPrListItem[]; now also populates real reviewRequests/isDraft/body
pr-existstea api repos/<repo>/pulls?state=all with nested .head.ref + .merged bool
issue-viewtea api repos/<repo>/issues/N+ a second call for the comments ARRAY (Gitea's issue object reports comments as an int count, which would crash consumers' .comments.filter(...))
recently-mergedtea api repos/<repo>/pulls?state=closed, filter .merged, use real .merged_at
issue-commenttea comments add (tea issues has no comment subcommand)

tea api needs an explicit owner/repo path segment (unlike tea <entity>,
which auto-detects it from the local git remote), and most concepts are invoked
withoutCODEV_REPO set (e.g. pr-exists receives only CODEV_BRANCH_NAME),
so each api-based script derives owner/repo from the origin remote, honoring
CODEV_REPO when present.

Left issue-search untouched: it isn't in the issue's broken list, and its only
difference from the working issue-list is an unverified body field — changing
working/unverified code on assumption would violate minimal-change.

Test Plan

Note on the full test suite

The full npm test run shows 9 failures in unrelated files
(team-update collectEvents ×7, team-github, team-cli) — all 5s-timeout I/O
that hangs in this resource-starved worktree/environment. Confirmed failing on
the clean base without these changes, so they are pre-existing and
environmental (they pass in CI). This PR touches none of those files.

🤖 Generated with Claude Code

pseudoseedand others added 4 commits July 6, 2026 13:06
The gitea preset invoked `tea <entity> list/view/whoami/comment`, whose
flattened `--fields` output (or missing flags/subcommands) doesn't match the
Gitea REST shape that forge-contracts.ts and the jq normalizers assume. Route
the read concepts through `tea api`, the raw REST passthrough that returns
exactly that shape:
- user-identity: `tea api user | jq .login` (`tea whoami` has no --output json)
- pr-view: `tea api repos/<repo>/pulls/N` → PrViewResult
- pr-list: `tea api repos/<repo>/pulls?state=open` → PrListItem[]
(now also populates real reviewRequests/isDraft/body)
- pr-exists: `tea api repos/<repo>/pulls?state=all` with nested .head.ref/.merged
- issue-view: `tea api repos/<repo>/issues/N` + a second call for the comments
ARRAY (Gitea's issue object reports `comments` as an int count,
which would crash consumers' `.comments.filter(...)`)
- recently-merged: `tea api repos/<repo>/pulls?state=closed`, filter .merged,
using the real .merged_at
- issue-comment: `tea comments add` (`tea issues` has no `comment` subcommand)
`tea api` needs an explicit owner/repo path segment (unlike `tea <entity>`,
which auto-detects it from the local git remote), and most concepts are invoked
without CODEV_REPO set, so each api-based script derives owner/repo from the
origin remote, honoring CODEV_REPO when present.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stubs a fake `tea` on PATH answering `api <endpoint>` with captured Gitea REST
fixtures (tea isn't in CI, per cluesmith#920), points the scripts at a throwaway repo
with a gitea remote, runs each real script, and asserts the normalized output
conforms to forge-contracts.ts — incl. comments-as-array, merged-only filtering,
open/merged/closed pr-exists cases, and CODEV_REPO override.
Also updates the cluesmith#568 pr-exists assertion for gitea to match the new
`state=all` query param (was `--state all` flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pseudoseed

Copy link
Copy Markdown
OwnerAuthor

Superseded by the upstream bugfix PR cluesmith#1146 (same branch, targeting the codev repo). Closing this fork-internal duplicate.

pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…cker, perf bound, CJS interop)
Iteration 2 of Phase 2, addressing the iter-1 3-way review (Gemini + Claude APPROVE, Codex REQUEST_CHANGES):
- Codex #1 (missing claude-picker fixture): add a synthesized claude `/model` picker fixture (claude-picker.busy.txt) whose highlighted row starts with the same ❯ glyph as the composer marker, with normal-intensity model names. Pins that a picker's selection-cursor + list classifies busy via the user-text path, never a false-clean; mirrors the real codex-picker capture (`› 1. …`). Wired into the required-states assertion; suite now 23/23. Documented as synthesized in the fixtures README (sandbox claude is the ez-cli shim, same reason as claude-idle).
- Codex #2 (perf assertion too loose): replace the single cold-run < 500ms with warm-up + best-of-5 min < 75ms. The min strips JIT/GC/scheduling noise (42.7ms cold vs 14.5ms native steady-state here), so it validates the spec's ≤~50ms seed-cap budget (measured best-of-5 = 19.2ms) instead of flaking. 5x tighter than before; 75ms is the CI-noise ceiling, not a near-budget claim (the logged value is the evidence).
- Bonus latent production bug, found while grounding the perf measurement against the compiled dist under native node: @xterm/headless resolves to its CommonJS entry (no exports map / type:module) with non-analyzable named exports, so `import { Terminal }` throws "Named export 'Terminal' not found" under native-node ESM — how the compiled bins run in production. Masked by vitest (vite interop) and dormant until Phase 4 wires the gate. Switch to the default-import form (codebase convention, cf. `import Database from 'better-sqlite3'`) plus a type-only alias for the one type-position use.
Refs cluesmith#1313.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pseudoseed pushed a commit that referenced this pull request Aug 14, 2026
…-ceiling+memo
All three reviewers (Gemini/Codex/Claude) returned REQUEST_CHANGES; all agreed the
over-ceiling removal itself is correct and shippable. Fixes:
- Memo stale-verdict across a same-agentKey PTY respawn / RingBuffer.clear() (all
three, HIGH): the ringToken (currentSeq:partialBytes:...) is only unique WITHIN one
monotonic ring, so a token can alias across session instances. CachedVerdict now
binds the live session instance too (hit = cached.session===session && token);
getSession(tid) is stable per live terminal, so it hits across ticks and misses
after a respawn. Test added.
- CPU regression: the memo does NOT help the expensive case (Claude #1; Codex OOM).
A BUSY held ring repaints every tick -> token changes every tick -> the memo always
misses exactly when the ring is largest (~230ms/tick/agent, await-serial). Added a
cost-aware backstop backoff: after a big (> BIG_RING_UNITS=4M) not-clean render, the
backstop skips re-classifying that agent for an exponential span (<=8 ticks). NEVER a
hold - scheduleDrain still classifies fresh the instant the line clears, so delivery
latency is unaffected. Test added.
- OOM doc corrected (Codex + Claude): the residual is a possible Tower OOM/crash
(unbounded allocation), not merely an event-loop stall (xterm chunks + yields). No
holding cap added (it would just reintroduce the outage); robust fix = off-thread
classify / cluesmith#1047 persistent xterm, out of scope.
- Interrupt Ctrl+C was OUTSIDE the submitToSession lock (all three): a concurrent
submission's Ctrl+C could kill another composer / run in the 100ms gap. Now the
Ctrl+C + settle (via writeMessageToSession delayOffset) + write are one atomic locked
section. Corrected the overstated anti-fusion claim (serializes interrupt-vs-escape
only, not vs a concurrent mailbox delivery).
- spec-1280 T16 predicate (all three): my manifest-dir-touch scoping silently skipped
the forgot-the-manifest-entirely case + had a Windows path.sep bug (always skipped).
Adopted Claude's portable predicate (/1280/ branch OR touches codev/projects/1280).
- stop() now clears verdictMemo/notCleanStreak/scheduledDrains/classifyBackoff
(Codex + Claude). cron test asserts target via objectContaining (Claude).
Deferred/flagged for the architect: off-thread/memory-bounded classify (cluesmith#1047); the
mailbox write edge taking the per-terminal lock to kill interrupt-vs-delivery fusion;
the interrupt-throw -> re-deliver duplicate (minor).
Full unit suite: 4261 pass / 48 skip / 0 fail. tsc clean.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitea forge preset is broken against the real tea CLI (0.14.2)

1 participant

@pseudoseed