Skip to content

feat: ecosystem architecture enhancements (Graph, Loop, Context, Harness)#120

Merged
Patel230 merged 70 commits into
mainfrom
feat/cli-quality-all
Jul 25, 2026
Merged

feat: ecosystem architecture enhancements (Graph, Loop, Context, Harness)#120
Patel230 merged 70 commits into
mainfrom
feat/cli-quality-all

Conversation

@Patel230

Copy link
Copy Markdown
Contributor

This PR introduces cross-ecosystem architectural enhancements encompassing Graph Engineering, Loop Engineering, Context Management, and Prompt Frameworks.

What's Included

  • Graph Engineering: StateGraph implementations, A2A protocol support, and Cypher-like DSLs.
  • Loop Engineering: Async exponential backoff and retry handlers with strict context timeout support.
  • Context Engineering: Token budget optimizers prioritizing recent messages and system prompts.
  • Harness Engineering: Test harness wrappers for injecting mocks and running result validations.
  • Prompt Engineering: Secure text templating engines for LLM prompts.

All changes include appropriate edge-case handling for timeouts, cancellations, and state immutability.

Patel230 added 30 commits July 23, 2026 01:32
- Add --quiet/-q global flag to suppress spinners, progress, and decoration
- Add StartPager/StopPager for long output (uses less/more via PAGER)
- Add per-stream TTY detection (stdin/stdout/stderr independent)
- Add IsQuiet/CanPrompt/ShouldColor/ShouldUnicode helpers in cmd/ui.go
- Deduplicate error classification: single source in internal/hawkerr
  with ClassifyExitCode and ClassifyErrorMessage both delegating to it
Prevents accidental cancellation of long-running operations. Users
must press Ctrl+C to cancel mid-turn, matching Grok CLI behavior.
All slash commands verified and fixed, resource leaks sealed, security
issues addressed, and UX polished across the entire TUI surface.

Security:
- Fix /rename path traversal (sanitize with filepath.Base)
- Fix /tag path traversal via session ID
- Zero Windows credential blob before freeing
- Add 5s timeout to clipboard commands (prevent hangs)

Resource leaks:
- /quit now cancels loopCancel, sleepCancel, stops watcher, clears tab progress
- /new cancels loopCancel and sleepCancel before resetting session
- TokenStore deprecated stub properly documented

Command correctness:
- /drop was silently ignored; now drops N exchanges with input validation
- /compact-mode alias collision resolved (aliases: nil)
- /session description corrected ("show current session info")
- /select description clarified
- /keybindings Ctrl+L now correctly says "Cycle autonomy tier"
- Six hidden commands added to autocomplete with descriptions

UX polish:
- Fix misleading Ctrl+K comment (was saying Ctrl+\)
- Sticky header overlay when scrolled up past user messages
- OSC 9;4 tab progress bar with proper indeterminate/clear handling
- Contextual tips shown on welcome screen
- Clipboard fallback to file when native tools unavailable
- JSON field filtering via --output-fields flag
- Project-scoped config walk-up (.hawk/settings.json)
- Minimal theme palette added
- Cosign keyless OIDC signing for releases
- Linux systemd-inhibit for sleep prevention
Bugs found during deep code review:

- Sticky header: panic on narrow terminals (viewWidth < 13) due to
  negative slice index; fixed with early guard
- Scrollbar: subtracted 1 from viewport height but sticky header
  renders 2 lines; now subtracts stickyHeaderHeight
- /drop: missing waiting guard could corrupt state during streaming;
  added guard plus loopCancel/sleepCancel cleanup
- /new: file watcher not stopped; now calls watcherStop
- Ctrl+K: selection mode handler blocked command palette input;
  reordered so palette check runs first
- auth.go: CredWrite exception leaked unmanaged memory; wrapped in
  try/finally to always free resources
- auth.go: getWindows passed credential secret via argv (visible
  to ps); now pipes script via stdin like setWindows
- install.sh: curl || true silently skipped signature verification;
  now aborts install on download failure; escaped dots in identity regex
- Notification: m.partial.Len() == 0 always true after flush;
  now uses turnHadAssistantOutput flag
- Added descriptions for 8 commands missing from slashDescriptions
- Notification never fired: turnHadAssistantOutput was reset before
  the check; now saved to local before reset
- UTF-8 data corruption: byte-based string slicing in sticky header
  and chat_print.go split multi-byte runes; now uses rune-based slicing
- Wayland clipboard: added wl-copy/wl-paste fallback alongside xclip/xsel
- Remove no-op TestThemeRegistryAllDarkOrLight (Go bools always true/false)
- Add 'minimal' to TestThemeNames expected list
- Fix misleading 'lowercased' comment in theme_palettes.go
…lidation

- genID: fall back to timestamp-based ID if CSPRNG fails
- prepareSession: warn when --session-id is ignored during resume/continue
- chat.go: surface plugin load failures to stderr; move log.SetOutput before tea.NewProgram
- chat_update.go/chat_submit.go: initialize startedAt with time.Now() to eliminate lazy-init race
- chat_view.go: make spinnerElapsed pure-read; fix wrapText narrow-terminal overflow
- chat_view.go: merge duplicate wrapText doc comment; clarify regex var comments
- chat_select.go: guard stdin raw-read with term.IsTerminal to prevent hang on non-TTY
- chat_subcommand_simple.go: invalidate slash suggestion cache after plugin reload
- completions.go: detect ARM Mac Homebrew prefix dynamically (/opt/homebrew then /usr/local)
- completions.go: GenerateJSON returns error instead of silent "{}"
- fuzzy_score.go: preserve category grouping in command palette fuzzy sort
- root.go: surface GenerateJSON error to stderr in completion command
…opy, export formats

- Ctrl+R: reverse-i-search overlay for input history with fuzzy matching
- Ctrl+S: session picker with fuzzy search and time-ago display
- 'c' key: copy code block nearest to viewport center in scrollback
- /export: support md, json, and txt format options
- Permission prompts: visual countdown bar for timeout awareness
- Status bar: two-line layout on wide terminals (≥120 cols)
- Welcome screen: recent sessions for quick resume
- Command palette: recent commands category and usage persistence
- Help: grouped by category, did-you-mean for unknown commands
- Tool results: collapsible with [+N lines] toggle in scrollback
Based on comparison with top CLI/TUI projects (fzf, lazygit, gh, k9s, ripgrep)
and research papers on CLI UX, agent systems, and LLM interfaces.

Performance:
- Fix codeBlockAtViewportCenter O(n) re-render: use cached viewport content
- Cache fullExpandedMap to avoid repeated allocations in viewport math

Memory:
- Add message count bounds (500 max) with pagination hint to prevent
  unbounded memory growth in long sessions

UX:
- Add visual feedback for code block copy (line count, fallback path)
- Add error feedback when no code block found at viewport center
- Enhance session picker with CWD context display
- Improve history search selection visibility with marker

Reliability:
- Cancel background goroutines on quit to prevent resource leaks

Accessibility:
- Add text alternatives for color-only indicators (countdown bar urgency)
…ng, UX

- Fix race condition in /parallel: use atomic counter for task index
- Fix resource leak in /parallel: use cancellable context, cancel on quit
- Cancel parallel agents on /quit, /exit, /clear, /new, /drop, Ctrl+C
- Fix /diff error handling: report git errors instead of silent failure
- Fix UX consistency: add selection marker to session picker (matches history search)
- Fix bounds check in formatSessionEntry: handle small maxWidth gracefully
…imeouts

- Fix permission prompt timeout: send denial response to unblock waiting goroutine
- Fix askUser prompt timeout: send empty response to unblock waiting goroutine
- Fix streamDoneMsg: resolve pending prompts before clearing state
- Fix streamErrMsg: resolve pending prompts before clearing state

Previously, when permission or askUser prompts timed out (or the stream
completed/errored), the response channels were never written to, causing
the goroutines waiting on those channels to hang indefinitely.
- trimOldMessages: preserve the leading welcome message. The old slice
  m.messages[startIdx+trimCount:] skipped index 0 and prepended only the
  hint, silently dropping the welcome header in long sessions. Now rebuilds
  as preserved-prefix + hint + recent messages.

- codeBlockFromCachedViewport: convert the absolute centerY (YOffset+Height/2)
  to a viewport-relative index before indexing viewport.View(), which returns
  only visible rows. Previously, when scrolled down, centerY was clamped to the
  bottom visible line and the wrong code block was selected for 'c' copy.

- codeBlockFromCachedViewport: stop using blocks[relativeCenter % len(blocks)],
  which picked an arbitrary block for multi-block messages. Now answers
  confidently only for the unambiguous single-block case.

- codeBlockAtViewportCenter: fall through to the exact slow path
  (findClosestCodeBlock on raw content) when the fast path is uncertain,
  instead of returning false and reporting 'no code block found'.

- /parallel: cancel any prior parallel run before starting a new one so only
  one runs at a time and quitting never orphans the previous run's agents
  (concurrent runs also clobber each other's grid display).
…UTF-8

- ResumeSession: persist a WAL-recovered session to JSONL before removing
  the WAL. Previously the WAL (the only durable copy when Load failed) was
  deleted unconditionally, so a crash before the next autosave lost the
  recovered messages. The WAL is now kept if the persist fails.
- BatchedWAL.flushLocked: on a mid-batch write error, keep only the
  not-yet-written suffix instead of the whole buffer, so the already-written
  prefix is not rewritten (and duplicated) on the next flush.
- Markdown italic (both the lipgloss and struct renderers): decode the
  boundary characters as full runes instead of single bytes, so multi-byte
  characters adjacent to '*' markers (e.g. café*…*, 日本語*…*) are no longer
  truncated/corrupted. Adds a regression test covering both renderers.
…eview

- export: redactToolArguments now recurses into nested maps and slices
  (redactValue), so secrets inside structured tool arguments such as
  {"env": {"API_TOKEN": …}} or {"keys": ["sk-…"]} are redacted instead of
  leaking. Also broaden the sk- pattern to allow hyphens/underscores so real
  Anthropic/OpenAI keys (sk-ant-api03-…, sk-proj-…) are caught. Adds a
  regression test that walks the whole redacted argument tree.
- fork: ForkThread no longer panics on a source thread ID shorter than 8
  chars when auto-generating the fork name (was SourceThreadID[:8]).
- rewind: truncatePreview truncates on a rune boundary (never splitting a
  multi-byte character) and builds the cleaned string via strings.Map instead
  of O(n²) concatenation. Adds fork and multi-byte preview regression tests.
compressFile ignored the output file's Close error, yet CompressOldSessions
deletes the original session once it returns nil. A failed final flush (e.g. on
network filesystems) could leave a truncated .gz while the original was
removed. Capture the Close error via a named return so a flush failure surfaces
to the caller (which then keeps the original) and discards the bad .gz.
IndexSession and FormatResults truncated previews with a byte-offset slice
(preview[:100], content[:showContext]), which splits a multi-byte rune and
produces invalid UTF-8 (a garbled trailing character). Route both through a
shared truncateUTF8 helper that backs off to a valid rune boundary. Adds a
regression test indexing CJK content and asserting the preview stays valid
UTF-8 within the byte budget.
…safe truncations in chat_welcome/bg_sessions/mentions
…session/handover + external sight/eyrie; guard fork/bg short-ID slices
…gnment in spec/autonomy pickers for multi-byte names
…ible/visibleLength in visual diff (no more split multi-byte runes / CJK misalignment)
…izing (hint contains multi-byte · and → symbols)
The session picker and history search highlighted matches by slicing the
original string at a byte offset found in a lowercased copy, using the raw
query's byte length. When case-folding changes a rune's encoded byte length
(e.g. İ, ẞ), the offset could land mid-rune, producing invalid UTF-8 or a
slice-out-of-range panic (e.g. entry "İtarget", query "target").

Add indexFold, a rune-wise case-insensitive search that returns a byte span in
the original string, so the highlight slice always falls on rune boundaries.
Behavior is unchanged for ASCII and strictly more correct for multi-byte input.
Covered by TestIndexFold, TestIndexFold_SpansAlwaysValidUTF8, and
TestHighlightMatch_MultibyteSafe.
…edback highlight offsets

Round 16 fixed the session-picker and history-search highlighters locally.
The same bug class — taking a byte offset from a lowercased copy and reusing it
to slice the original string — existed in four more hawk-module sites, where it
could splice text at a non-rune boundary (garbled UTF-8) or panic when
case-folding changed a rune's encoded length:

- internal/session HighlightMatches: FTS result highlighting spliced "**" into
  the original content at offsets derived from the lowercased copy.
- cmd ghStripMention: stripped the @hawk mention from GitHub comment bodies.
- internal/engine LearnFromFeedback: parsed "don't"/"always" rules from user input.
- internal/engine/intelligence Adapt: parsed the "needed <tool>" feedback pattern.

Promote the rune-wise case-insensitive search to textutil.IndexFold (exported)
and route all five call sites through it, so every returned span refers to the
original string and falls on rune boundaries. cmd's indexFold is now a thin
wrapper. Behavior is unchanged for ASCII input. Adds textutil.IndexFold tests.
Bump external/sight to 64e2f31, which fixes the same case-fold byte-offset bug
class addressed in hawk rounds 16-17: parseFilterResponse now locates the
"reasoning:" keyword with a rune-wise case-insensitive search (indexFold) so
extracting the reasoning never slices the response at a non-rune boundary when
case-folding changes a rune's encoded length. Completes this bug class across
the hawk module and the sight submodule.
Bump external/tok to b2d4c8b2, which makes the compaction layer's extractFocus
rune-safe: the verb-centered context window and the default 100-char truncation
now operate in runes (via a rune-wise case-insensitive indexFold) instead of raw
byte offsets, so multi-byte conversation content no longer produces invalid
UTF-8 in snapshot focus strings. ASCII behavior is unchanged. This completes the
rune-safety / case-fold bug class across the hawk module and the sight and tok
submodules.
Six core files (chat.go, chat_commands.go, chat_export.go, chat_model.go,
command_history.go, statusbar.go) carried pre-existing gofmt deviations —
mostly var blocks whose later additions broke the canonical '=' alignment.
Running gofmt -l on cmd/ and internal/ now returns empty, so the tree passes
the standard Go formatting gate and future formatting regressions are no longer
masked by existing noise. Whitespace-only; build, vet, and the cmd test suite
all pass unchanged.
The /voice handler spawned a raw goroutine that appended to m.messages and
called m.input.SetValue/CursorEnd directly, racing the Bubble Tea update loop
which reads and writes the same model state on its own goroutine — undefined
behavior that could corrupt the message slice or crash.

Route the background work through the runtime instead: Handle now returns a
tea.Cmd (recordAndTranscribe) that performs the recording and transcription and
reports the outcome as a voiceResultMsg. A new case in the update loop applies
the result — injecting the transcript into the input or showing the appropriate
system/error message — so all model mutation happens on the Bubble Tea
goroutine. This mirrors the existing async-message pattern (modelsFetchedMsg,
streamChunkMsg). Behavior is preserved, including message roles and the
empty-transcript no-op. Adds TestVoiceResultMsg.
Patel230 added 28 commits July 23, 2026 20:42
- Add 'hawk bug-report' that prints a redacted diagnostic report (version,
  platform, Go version, doctor output) suitable for pasting into GitHub issues
- Expand rootCmd.Long from one sentence to a full orientation paragraph with
  quick-start entry points and credential setup guidance
Replace the hand-maintained OPTIONS list (already stale, missing ~12 flags)
with iteration over rootCmd.Flags().VisitAll(). The man page now always
reflects the actual CLI surface. Also adds a COMMANDS section generated
from registered subcommands, and updates the DESCRIPTION to match the
expanded --help text.
Document all completion shells (bash/zsh/fish/powershell), the
'hawk completion install' auto-installer, manual setup instructions,
and the JSON spec for IDE integration.
…ssion output

Adds a --json flag to 'hawk sessions ls' that outputs background
session metadata as indented JSON instead of formatted text cards.
'hawk eval list --json' outputs all benchmark tasks as JSON.
'hawk eval results --json' outputs all saved evaluation results as JSON.
Both mirror the existing --output json support on 'hawk eval run'.
…tput

'hawk trust list --json' outputs trusted directories as a JSON array
with path, trusted_at, and reason fields. Empty store returns '[]'.
…tection

'hawk rules detect --json' outputs the detected AI tool rule files as
a JSON object mapping format names to file paths.
Adds JSON tags to PathCheck and DeveloperPathReport structs and a
--json flag to 'hawk path' that outputs the readiness report as
indented JSON instead of the formatted text report.
'hawk daemon status --json' outputs daemon status as JSON with
status, pid, address, and started_at fields. Handles not-running and
stale-PID-file cases with appropriate JSON envelopes.
'hawk snapshot list --json' outputs snapshots as a JSON array.
'hawk snapshot diff <hash> --json' outputs file diffs as a JSON array.
Both leverage the existing JSON tags on Patch and FileDiff structs.
'hawk plugin list --json' outputs installed plugin manifests as a JSON
array, leveraging the existing JSON tags on the Manifest struct.
'hawk tools --json' outputs built-in tools as a JSON array of objects
with name and description fields.
'hawk feedback --json <message>' outputs the FeedbackReport as indented
JSON to stdout instead of opening a browser or saving locally. The
FeedbackReport struct already had JSON tags.
Adds BuildEcosystemReport returning a structured EcosystemReport with
eyrie/yaad/tok sub-objects. 'hawk ecosystem --json' outputs this as
indented JSON instead of the formatted text panel.
'hawk plan list --json' outputs all saved plans as a JSON array.
'hawk plan show <name> --json' outputs a single plan as JSON.
Both leverage the existing JSON tags on the Plan struct.
The CatalogHealth struct now serializes to JSON with snake_case field
names, enabling machine-readable catalog health output.
Adds machine-readable JSON output to 'hawk checkpoint list', consistent
with the --json flag pattern used across other hawk commands (snapshot
list, sessions ls, bg ls, etc.).
The shared catalogtest fixture now ships 9 gateways with models (20
total), so the skip guards (len(rows)<2, len(rows)==0, h.Models==0)
never trigger — the tests already pass with real assertions. Removing
the dead branches and the misleading 'FIXME: test skipped' comments
makes the actual coverage visible and asserts unconditionally.
…ersion

Add machine-readable JSON output to three more hawk subcommands:

- agent list --json: emits the agent slice as JSON, normalizing an
  empty result to [] instead of null so jq-friendly consumers work.
- cost analyze --json / cost summary --json: emits the
  OptimizationReport as JSON, suppressing the experimental notices
  for clean machine parsing.
- version --json: emits {version, build_date} using a dedicated
  versionInfo struct with build_date omitted when empty.
- agent_test.go: JSON output for empty and populated agent lists,
  verifying the empty case yields [] not null and omitempty on model.
- cost_test.go: JSON output for analyze/summary (empty report) plus
  a guard that text mode does not start with '{'.
- version_test.go: JSON output with and without build_date, the
  omitempty contract, and a text-mode guard.
PreflightReport is already JSON-tagged (ready, checks). Wire up a
--json flag that emits the raw report instead of the formatted text,
so CI/scripts can parse readiness and inspect individual checks. The
readiness error is still returned after JSON output, preserving exit
code semantics. Includes JSON-structure and text-mode tests.
- Add ExecutionGraph implementation with observations and tests
- Add GraphJournal for tracking execution flows
- Add cloud_graph and execution_graph commands with tests
- Add graph domain types and routes
- Update submodules (eyrie, hawk-core-contracts, inspect, sight, tok, trace, yaad)
- Update README
- eyrie: 2c21d5e (feat: add graph module)
- hawk-core-contracts: 90fa2c5 (feat: add graph module)
- inspect: 079625e (feat: add quality graph)
- sight: 56c98b4 (feat: add quality graph)
- tok: ee8b8f2 (feat: add runtime graph)
- trace: b6c6553 (feat: add graph command)
- yaad: 0cdbd7b (feat: add portable graph)
- Add StateGraph implementation for agent orchestration
- Add Node, Edge, and GraphState types
- Implement conditional routing and checkpointing
- Support time-travel debugging
- Add Python pytest suites for TestHarness, ContextOptimizer, and GraphQuery DSL
- Fix executiongraph StateGraph failing unit tests in Go
- Ensure 100% pass rate on core graph execution paths
@Patel230
Patel230 merged commit 9175bcd into main Jul 25, 2026
9 of 16 checks passed
@Patel230
Patel230 deleted the feat/cli-quality-all branch July 25, 2026 10:25
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