Skip to content

Release v3.3.0: develop → main - #239

Merged
nugehs merged 21 commits into
mainfrom
develop
Sep 27, 2026
Merged

nugehs merged 21 commits into
mainfrom
develop

Conversation

@nugehs

@nugehs nugehs commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Merging develop into main. No version bump — package.json stays 3.2.0 on both sides, so this does not trigger the auto-tag-release workflow or an npm publish.

Notable since the last develop→main merge (#229):

Test plan

  • Both branches confirmed at the same package.json version (3.2.0) — no release will fire
  • npm run typecheck / npm run lint / npm test (787/787) all green on develop as of the latest merge
  • CI on this PR (below)

… glyphs, hook policy and action majors

Phase 1 of the Vibe output plan. The default terminal output does not change.

- createRenderer resolves one glyph set (emoji | ascii | unicode) and exposes
  it as renderer.glyphs and renderer.glyphMode. unicode is reachable only
  through the explicit `glyphs: "unicode"` option and carries no
  Extended_Pictographic mark. New string builders: table, tree, flow, code,
  ref, list, phase, close and paint; magenta and blue join the palette;
  visualWidth, padRight, wrap and wrapBoxed are exported.
- visualWidth measures box drawing, arrows and the tick as one cell (real wide
  ranges, Emoji_Presentation and U+FE0F) instead of everything from U+1100 up
  as two.
- header and verdict wrap long content to the inner width instead of breaking
  the right border. Content that fits is byte-identical to before, pinned by
  tests/fixtures/render-fancy-golden.json, generated from develop at 1ac7087.
- The three eval headers passed "\\u{1F9EA}" as a literal and printed the
  escape sequence in emoji mode.
- The generated pre-commit hook gates under --policy standard, so a company or
  high-risk policy in .otitorc.json no longer fails every local commit.
- The init workflow template pins actions/checkout, setup-node and
  upload-artifact at v7, the majors otito-ci.yml runs on, with a drift test.
feat(render): glyph modes, Vibe primitives and box wrapping; fix eval glyphs, hook policy and action majors
…lden snapshots

Phase 2 of the Vibe output plan. Nothing visible changes.

- The ~50 `renderer.emoji ? "…" : "…"` ternaries in context-engine, impact,
  pass-local, pass-pr, review, render/route and output.js now read
  renderer.glyphs (item, tip, box.arrow, status, verdict) or ask the new
  renderer.pick({ emoji, ascii, unicode }) for a one-off decoration, so every
  mark follows the glyph mode instead of one boolean (O7). renderer.emoji is
  no longer read outside fancy.js; a test scans src/lib to keep it that way.
- otito report builds a renderer from the same --emoji/--color/--theme
  preferences every other command honours (O9); the report prints no glyphs
  yet, so its output is unchanged.
- tests/fixtures/formatters-golden.json records every terminal formatter's
  output for stored input in emoji and ascii modes, with and without colour,
  generated by tests/golden/generate-formatters.mjs on the formatters as they
  were before this change (O10). tests/formatters-golden.test.js re-renders
  the stored input and compares byte for byte.
refactor(render): take every formatter's marks from the glyph set; golden snapshots
… and boxes

Phase 3 of the Vibe output plan: the default flips and the shared layers
take the Vibe shape.

- The default glyph set is unicode: no emoji in an interactive terminal.
  CI, NO_EMOJI, --no-emoji, emoji: false, the minimal theme and, new,
  TERM=dumb keep ASCII; --emoji, emoji: true and OTITO_EMOJI=1 opt back into
  the emoji look. The high-contrast theme keeps its bright palette and stops
  forcing emoji. TERM=dumb is detected in fancy.js, not in config.
- O13: header and verdict content lines are padded to width - 6, so every
  line of a box is as wide as its borders. Both golden fixtures were
  regenerated on purpose; in the formatter fixture the bespoke formatters
  change only in box content lines, two cells narrower, same text.
- formatTerminalSummary prints facts as a table under "At a glance", file
  lists as a tree, other sections as "- " lists, and the closing line last.
  Writers are verified by the handler that wrote, so formatters stay pure.
- renderDocument aligns Markdown tables by padding (O11), only when every
  row has the same number of cells after honouring \| and code spans, and
  appends meta.close. printDocument commands pass their closing line.
- config list/get, telemetry, deps, structure, attest, dashboard and
  obsidian print in the shared shape and end with a closing line.
- otito help documents --emoji, --no-emoji, --color, --no-color and --theme
  once, as global flags.
feat(render): plain Unicode by default, closing lines, aligned tables and boxes
…e grade

Claude Code delivers a finished background task through UserPromptSubmit,
and the route-prompt hook routed it like a request. On 2026-09-27, 79 of
the 146 decisions in ~/.otito/route-decisions.jsonl were prompts the
harness wrote, matched by hash to the queue records in the transcripts:
75 <task-notification>, 3 <bash-input> and 1 <ci-monitor-event>.

isRoutable now returns false for those three. A <system-reminder> in
front of a prompt is looked past, because all 22 logged prompts that
opened with one had a request behind it. <pasted_content>,
<create-pr-command> and <cross-session-message> still route.

route-outcomes hashes the harness prompts it finds in each session's
user and queue records, leaves decisions with those hashes out of the
grade and reports the count. A harness prompt in a transcript is no
longer a prompt or a follow-up. The log itself is untouched.
fix(route): skip harness prompts in the hook and leave them out of the grade
…cript

A context pack for bashbop-event-web listed `yarn lint`, `yarn test`,
`yarn test:e2e` and `yarn build` and no type check: its type check is
`tsc:check`, which the harness's fixed script list did not name and whose
reason `name.includes("type") || name === "tsc"` did not match. `otito pr`
had `tsc:check` on its list and dropped it for want of a reason, and the
quality job and pre-commit hook from `otito init` never saw it.

The harness, the PR review and init now share src/lib/package-scripts.js.
A script is a type check when `tsc`, `typecheck`, `type-check` or
`check-types` are whole segments of its name (`tsc:check`, `check:tsc`,
`tsc-check`, `tscCheck`), so `tsconfig:sync` and `prototype` are not.
A package with none of the listed type-check names gets every type check
it defines in the list's type-check slot; one that has one gets the same
list as before. Watch and build variants, and a type check run with
`--watch` or `tsc -w`, are left out.

An end-to-end script gives way to its headless sibling when the package
has one, so event-web's pack lists `yarn test:e2e:headless` instead of
`playwright test --headed`.
fix(harness): list tsc:check-style type checks and the headless e2e script
…okens, list runnable tests only

Four ranking defects found reviewing otito's use on bashbop-event-web
(2026-09-27), fixed once for context_pack and change_impact in
src/lib/ranking-rules.js:

- Translation catalogs are demoted (x0.3) unless the request is about copy,
  wording, i18n or translation, and the locales of one catalog share one
  entry (`siblings`); folded locales keep the catalog's role.
- The extension of a file named in the request no longer scores.
- A path or basename named in the request is pinned above unnamed candidates
  as a required owner; a named symbol pins its exported, imported definition
  and boosts its other definitions.
- Tests and suggested tests list only files a runner executes.

contextEngineVersion and impactEngineVersion 3 -> 4. New fixture
evals/fixtures/multi-locale-web and retrieval case; the 21 earlier cases are
unchanged.
fix(ranking): pin named files, fold locale catalogs, drop extension tokens, list runnable tests only
… budget

"an MCP tool call reaches a live canvas without waiting on it" asserted
elapsed < 900 ms, which timed the whole context_pack dispatch. Under
coverage with 64 copies in parallel it failed 6 of 64 runs (911-996 ms)
while the canvas tap behaved correctly.

A response that waits on the canvas cannot arrive while the send is held:
the canvas only replies after release(), so the send settles first, at
the latest when the tap aborts it after SEND_TIMEOUT_MS. The test now
wraps fetch to record the canvas send's state, with handlers attached
before the tap's, and asserts the send is still pending when the response
is written. It then releases the canvas and awaits the send, which
replaces the 10 ms polling loop before the received assertions.

Against a mutant where callTool awaits the send, the test fails with
"send failed: TimeoutError". A canvas-side "answered" flag alone passes
against that mutant, because release() only runs in t.after.
test(canvas-tap): assert the held send is still pending, not a 900 ms budget
…sleading doctor next-step

otito install now prompts a human at a TTY to choose plan/global/link mode,
opening with a short pitch (greeting pulled from local git config user.name,
no network) on why otito's grounded, deterministic local context is useful.
--yes, --json, CI, agents, and any explicit --global/--link stay fully
non-interactive, unchanged.

Also drops "run otito doctor to verify the install" from install's next
steps: doctor never checks the otito binary itself, only unrelated tools
(node, git, gh, rg, npx, opensrc, code-structure); the install step already
verifies its own binary via commandExists.
feat(install): interactive TTY setup with personalized pitch, drop misleading doctor next-step
…tion, not just headers

bullet() and every formatter that hand-built a line as
`${renderer.glyphs.item} text` (the shared summary, context_pack's
Commands, change_impact's suggested tests/risk hotspots, pass/pass-pr's
context evidence) now dim the marker when colour is on, matching how a
box border is dimmed. Colour off (piped output, NO_COLOR, CI) renders
byte-identical to before.

Regenerated tests/fixtures/render-fancy-golden.json and
tests/fixtures/formatters-golden.json; reviewed the diff, every changed
line is a bullet/item marker gaining \x1b[2m...\x1b[0m and nothing else moved.
Inspired-by-not-copied-from, but the name itself adds nothing a reader
needs; reworded the two in-repo mentions to describe what the renderer
work actually is instead of naming it.
feat(render): dim list and bullet markers so colour reaches every section
Minor release: renderer colour/tables/glyph-mode primitives, list and
bullet items now dim their marker, otito install prompts interactively
at a TTY with a personalized pitch. No command, field, or schema removed.

CHANGELOG.md: [Unreleased] entries moved under [3.3.0] - 2026-09-27.
package.json/package-lock.json bumped via `npm version minor
--no-git-tag-version`; server.json, docs/index.md, RELEASE.md and
docs/assets/otito-how-it-works.html resynced by the version script.
@nugehs nugehs changed the title Release: develop → main Release v3.3.0: develop → main Sep 27, 2026
@nugehs
nugehs merged commit fec3bd0 into main Sep 27, 2026
5 checks passed
Sign up for free to 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.

1 participant