Skip to content

feat(forge_pheno_memory): wire thegent-memory v2 polyglot facade (PR 4 of the 3-PR sequence, ADR-096)#3559

Closed
KooshaPari wants to merge 55 commits into
tailcallhq:mainfrom
KooshaPari:feat/forge-pheno-memory-2026-06-23
Closed

feat(forge_pheno_memory): wire thegent-memory v2 polyglot facade (PR 4 of the 3-PR sequence, ADR-096)#3559
KooshaPari wants to merge 55 commits into
tailcallhq:mainfrom
KooshaPari:feat/forge-pheno-memory-2026-06-23

Conversation

@KooshaPari

Copy link
Copy Markdown

Summary

This is the contribution-back step of the 3-PR forgecode improvement sequence (ADR-096, accepted 2026-06-23). It adds a new workspace member crates/forge_pheno_memory that wires the thegent-memory v2 polyglot facade (supermemory + letta + cognee + mem0) into forgecode's Infra + Domain pattern.

Forgecode agents can now use a stable, scope-routed memory API without coupling themselves to any specific memory engine.

Companion repos

  • Plugin bundle (fork of the user-side installation): KooshaPari/pheno-forge-plugins v0.1.0 — 6 systemd-managed sidecar plugins (supermemory, letta, cognee, mem0, config, tracing) brought up at machine boot.
  • Memory substrate (the v2 polyglot facade this PR consumes): KooshaPari/thegent#1144 (merged) — MemoryPort trait + 4 adapters + CompositeAdapter router.
  • C-ABI bridge (for non-Rust consumers): KooshaPari/pheno-cdylib-bridge v0.1.0 — 8 C symbols, used by the C smoke test in c/examples/smoke.c.

What this PR adds

New crate: crates/forge_pheno_memory/

use forge_pheno_memory::{PhenoMemoryService, PhenoMemoryScope};
use thegent_memory::v2::{MemoryValue, MemoryQuery};

let svc = PhenoMemoryService::with_defaults();

svc.store(PhenoMemoryScope::Episodic.into(), "k", "v".into()).await?;
let recs = svc.recall(
    PhenoMemoryScope::Episodic.into(),
    MemoryQuery::new("q"),
).await?;

Scope routing (locked per ADR-096)

MemoryScope Backing engine Default endpoint
Episodic supermemory (smfs filesystem) http://127.0.0.1:3030
Identity letta (subconscious blocks) http://127.0.0.1:8283
ProjectKnowledge cognee (knowledge graph) stdio cognee-mcp
Fallback mem0 http://127.0.0.1:8000

Errors

PhenoMemoryError is a thiserror::Error enum that surfaces MemoryError from the underlying v2 port. Variants: Network, Backend, NotFound, Serde, Unavailable, Invalid, Internal.

Test results

test result: ok. 3 passed; 0 failed   (config_defaults, service_constructs, scope_round_trip)

Why this is additive

  • thegent-memory is a path dep (can be swapped for a crates.io dep once it's published).
  • forge_pheno_memory is a new workspace member; no existing crate is modified.
  • No new build dependency is required to compile the rest of the workspace (the new crate is opt-in via workspace member).

Refs

  • ADR-096: docs/adr/2026-06-23/ADR-096-forgecode-improvement.md
  • Master plan: findings/2026-06-23-forgecode-improvement-plan.md
  • Spec: thegent/docs/specs/memory/v2.md (memory v2 polyglot facade)
  • PR 1 (user-side): KooshaPari/pheno-forge-plugins v0.1.0
  • PR 2 (memory substrate): KooshaPari/thegent#1144 (merged)
  • PR 3 (C-ABI for non-Rust consumers): KooshaPari/pheno-cdylib-bridge v0.1.0

KooshaPari and others added 30 commits May 1, 2026 20:06
- Bootstrap deny.toml with license allowlist + advisory ignores
- Add license = MIT to workspace.package (was missing)
- Add license.workspace = true to all 27 crate manifests
- Ignore transitive unmaintained (bincode, yaml-rust, paste, rustls-pemfile)
- Ignore transitive vulns (hickory-proto, rustls-webpki) via aws-sdk/reqwest

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Pin all action refs to immutable SHAs across workflow files:
- checkout@v4 → @11bd71901bbe5b1630ceea73d27597364c9af683
- checkout@v6 → @de0fac2e4500dabe0009e67214ff5f5447ce83dd
- setup-node@v4/v5, setup-python@v4/v5, setup-go@v5
- upload-artifact@v4/v7, download-artifact@v4
- cache@v3/v4, github-script@v7
- configure-pages@v5/v6, deploy-pages@v4/v5
- upload-pages-artifact@v3/v5, dependency-review-action@v4

Fixes version-tag normalization (add v4/v5 tags where missing).
Fixes double-SHA corruption artifacts from prior patching rounds.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reduce README from 1124 to 169 lines (-85%).
Keep: project name, description, quickstart, usage examples, why forge,
installation, community, documentation link.
Add fork disclaimer pointing to upstream tailcallhq/forgecode.
Preserve all upstream content via pointer comment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reduce README from 1124 to 169 lines (-85%).
Keep: project name, description, quickstart, usage examples, why forge,
installation, community, documentation link.
Add fork disclaimer pointing to upstream tailcallhq/forgecode.
Preserve all upstream content via pointer comment.

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Verified resolved upstream; advisory no longer triggers.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…nale

- Add 4 ignore entries for bincode 1.x (RUSTSEC-2025-0141), paste
  (2024-0436), rustls-pemfile (2025-0134), yaml-rust (2024-0320) — all
  transitive via upstream forgecode workspace deps; resolution depends on
  upstream tailcallhq/forgecode bumps.
- Pre-existing fork-specific RUSTSEC-2026-* ignores preserved.
- cargo deny check advisories: PASS.
…nale (#8)

- Add 4 ignore entries for bincode 1.x (RUSTSEC-2025-0141), paste
  (2024-0436), rustls-pemfile (2025-0134), yaml-rust (2024-0320) — all
  transitive via upstream forgecode workspace deps; resolution depends on
  upstream tailcallhq/forgecode bumps.
- Pre-existing fork-specific RUSTSEC-2026-* ignores preserved.
- cargo deny check advisories: PASS.

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Root cause: crossterm's cursor position CSI query times out (2s) when
multiple concurrent sessions are running or terminal is under load.

Fix:
- Add error::is_cursor_timeout_error() to detect cursor position errors
- Add terminal::get_cursor_position_with_retry() with backoff
- Suppress cursor errors during shutdown in Ui::shutdown()
- Add comprehensive tests for cursor error detection

Fixes session crashes where user sees:
  'cursor position could not be read within a normal duration'
  'Resource temporarily unavailable (os error 35)'

Tested: 337 tests pass (333 existing + 4 new cursor error tests)
Add summarization feature with:
- llm_summarizer: Async LLM-based summarization service
- adaptive_eviction: Importance-based eviction strategies
- metrics: Summarization metrics tracking
- prefilter: Pre-summarization filtering
- Updated compaction config and strategy

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…p-go

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Restore the workspace members array by listing all crate directories
present on disk, removing invalid glob patterns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add [bans] section with recommended warnings
- Update GitHub workflow files (trufflehog, stale, labels, release)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phenotype Agent and others added 25 commits May 28, 2026 04:00
Sync with tailcallhq/forgecode upstream. Resolved conflicts:
- Cargo.toml: keep MIT license, version 0.1.1, rust-version 1.92
- README.md: keep Phenotype fork header
- Cargo.lock: accept upstream versions for all conflicted deps

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: Phase0-4 modernization — hexagonal refactor, tests, governance, CI

* chore(forgecode): shell-script hygiene

Normalize Bash shebangs and enable strict shell mode for safer script execution.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Align [*] indent_style to tab per org-majority tick 20 audit.

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Align Cargo.toml [workspace.package].version and package.json to 2.9.9.

Tick 25 cargo version-drift audit detected Cargo.toml 0.1.1 vs package.json 1.0.0 vs latest tag v2.9.9. This commit brings both files into agreement and retargets v2.9.9 to this commit.

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Co-authored-by: Phenotype Agent <agent@phenotype.ai>
)

L1.4 governance keystone per L1 audit 2026-06-11
…tion, and loop commands (#20)

* fix: pin reedline to 0.47.0 (tailcallhq#3398)

Co-authored-by: ForgeCode <noreply@forgecode.dev>
Co-authored-by: laststylebender <43403528+laststylebender14@users.noreply.github.com>

* chore(deps): update rust crate reedline to v0.48.0 (tailcallhq#3406)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Revert "chore(deps): update rust crate reedline to v0.48.0" (tailcallhq#3409)

* fix(openai_responses): handle codex response completed/incomplete events (tailcallhq#3405)

* chore(deps): update rust crate posthog-rs to v0.7.2 (tailcallhq#3410)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency @ai-sdk/google-vertex to v4.0.140 (tailcallhq#3412)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency ai to v6.0.192 (tailcallhq#3413)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(provider): add model entries to provider.json and vertex.json (tailcallhq#3414)

* chore(deps): update rust crate posthog-rs to v0.7.3 (tailcallhq#3415)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate aws-sdk-bedrockruntime to v1.132.0 (tailcallhq#3416)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: apply Opus 4.7 API contract to Claude Opus 4.8 (tailcallhq#3418)

Co-authored-by: ForgeCode <noreply@forgecode.dev>

* refactor(editor): replace reedline with rustyline completer (tailcallhq#3399)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* fix(minimax): MiniMax M3 model support (tailcallhq#3434)

Co-authored-by: ForgeCode <noreply@forgecode.dev>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* fix(gemini): strip propertyNames from tool schemas (tailcallhq#3426)

Co-authored-by: Amit Singh <amitksingh1490@gmail.com>

* fix(http): map invalid response status to openai error (tailcallhq#3439)

* chore(deps): update aws-sdk-rust monorepo (tailcallhq#3420)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency @ai-sdk/google-vertex to v4.0.142 (tailcallhq#3440)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency ai to v6.0.197 (tailcallhq#3444)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency tsx to v4.22.4 (tailcallhq#3427)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate chrono to v0.4.45 (tailcallhq#3445)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate diesel to v2.3.10 (tailcallhq#3446)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate unicode-segmentation to v1.13.3 (tailcallhq#3431)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate uuid to v1.23.2 (tailcallhq#3421)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency @types/node to v24.13.0 (tailcallhq#3447)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust to 1.96 (tailcallhq#3419)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate ignore to v0.4.26 (tailcallhq#3453)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate google-cloud-auth to v1.12.0 (tailcallhq#3449)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate serial_test to v3.5.0 (tailcallhq#3423)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update rust crate posthog-rs to 0.9.0 (tailcallhq#3451)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update rust crate posthog-rs to 0.10.0 (tailcallhq#3455)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(provider): Ambient as a built-in verified-inference provider (tailcallhq#3389)

Co-authored-by: Amit Singh <amitksingh1490@gmail.com>

* chore(deps): update dependency @types/node to v24.13.1 (tailcallhq#3458)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>

* build(deps): bump brace-expansion from 5.0.5 to 5.0.6 (tailcallhq#3359)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(openai_responses): preserve 503 retryable errors in stream (tailcallhq#3460)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* fix(select): ignore key Release events so pickers do not close instantly on Windows (tailcallhq#3462)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* fix(editor): strip ANSI from rustyline prompt raw() so the cursor tracks on Windows (tailcallhq#3461)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>

* chore(deps): update tokio-prost monorepo to v0.14.4 (tailcallhq#3467)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency ai to v6.0.198 (tailcallhq#3470)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate http to v1.4.2 (tailcallhq#3471)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate uuid to v1.23.3 (tailcallhq#3473)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency @ai-sdk/google-vertex to v4.0.143 (tailcallhq#3475)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency ai to v6.0.199 (tailcallhq#3476)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(anthropic):  support for claude mythos and fable models (tailcallhq#3474)

* chore(deps): update rust crate regex to v1.12.4 (tailcallhq#3478)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency ai to v6.0.200 (tailcallhq#3481)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency @types/node to v24.13.2 (tailcallhq#3483)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency ai to v6.0.201 (tailcallhq#3484)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate insta to v1.48.0 (tailcallhq#3486)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency @ai-sdk/google-vertex to v4.0.144 (tailcallhq#3488)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency ai to v6.0.202 (tailcallhq#3489)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate rmcp to v1 [security] (tailcallhq#3277)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* build(deps): bump openssl from 0.10.78 to 0.10.80 (tailcallhq#3364)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>

* fix(config): config auto_install_vscode_extension option (tailcallhq#3485)

Co-authored-by: laststylebender <43403528+laststylebender14@users.noreply.github.com>

* chore(deps): update rust crate aws-smithy-types to v1.5.0 (tailcallhq#3490)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate async-openai to 0.41.0 (tailcallhq#3078)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* feat(provider): add claude-fable-5 to vertex_ai_anthropic models (tailcallhq#3480)

Co-authored-by: akhilapp <akhilapp@google.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>

* chore(deps): update rust crate google-cloud-auth to v1.13.0 (tailcallhq#3491)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update rust crate posthog-rs to 0.11.0 (tailcallhq#3487)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(forge_select): enter alternate screen to keep prompt visible (tailcallhq#3492)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* fix(zsh): pad _forge_reset to avoid zle clearing output (tailcallhq#3494)

* chore(deps): update dependency @ai-sdk/google-vertex to v4.0.145 (tailcallhq#3495)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update rust crate posthog-rs to 0.12.0 (tailcallhq#3498)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency ai to v6.0.204 (tailcallhq#3496)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update rust crate aws-sdk-bedrockruntime to v1.134.0 (tailcallhq#3499)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(provider): add missing fireworks models in provider.json (tailcallhq#3504)

* fix(provider): add z.ai glm-5.2 model to provider.json (tailcallhq#3505)

* chore(deps): update dependency ai to v6.0.205 (tailcallhq#3509)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: show only direct conversation initiated by the user via the `:conversation` command (tailcallhq#3510)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* feat: add parent_id, source, FTS5, subagent hiding, and loop commands

* fix(ui): apply user_initiated_conversations filter to SelectCommand::Conversation

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>
Co-authored-by: ForgeCode <noreply@forgecode.dev>
Co-authored-by: laststylebender <43403528+laststylebender14@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Imamuzzaki Abu Salam <imamuzzaki@gmail.com>
Co-authored-by: Pascal <git@pascalbrokmeier.de>
Co-authored-by: Gregory <205641639+ambient-gregory@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: resrever <scottyoung@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Sandipsinh Dilipsinh Rathod <zotbysandip@gmail.com>
Co-authored-by: Akhil Appana <akhil.appana@gmail.com>
Co-authored-by: akhilapp <akhilapp@google.com>
Co-authored-by: Tushar Mathur <tusharmath@gmail.com>
Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…y-ref upsert, HTTP cache, composite indexes (#21)

* feat: add L7-001 intent+boundary snapshot docs

Propagated from KooshaPari/phenotype-registry @ chore/l7-001-curation-snapshot (a1aa44660).

* chore: tier-0 hygiene snapshot 2026-06-20

* fix(forge_config): correct OutputSettings::render for compact blank-line collapse

The previous implementation preserved a blank line between content lines
and always added a trailing newline regardless of the trailing_newline
setting, causing 2 of 8 output tests to fail.

The corrected implementation:
- Skips blank lines entirely (collapses them in compact mode)
- Honors the trailing_newline setting (no newline when disabled)
- Preserves the trim-each-line behavior

All 8 output tests now pass; 42 of 42 forge_config tests green.

* chore: tier-0 hygiene snapshot 2026-06-20

* fix(forge_config,forge_repo): wire up OutputMode + ConversationRepository

forge_config/output.rs:
- Add OutputMode::label() returning a static str (used by status messages)

forge_repo/conversation_record.rs:
- Add QueryableByName derive so ConversationRecord can be loaded by
  diesel::sql_query (used by FTS5 search_conversations)

forge_repo/forge_repo.rs:
- Add missing ConversationRepository impl methods on ForgeRepo<F>:
  - upsert_conversation_ref
  - search_conversations
  - optimize_fts_index
  All delegate to self.conversation_repository (the inner impl).

forge_repo/conversation_repo.rs (already done in earlier session):
- Clone the conversation before the Send+'static closure to fix the
  'borrowed data escapes outside of method' error.

With these fixes the full workspace (forge_config + forge_repo + forge_main
+ downstream) compiles cleanly. forge_config has 42/42 tests passing.

* fix(forge_app): replace shadowed self with snapshot Drop guard

The prior OrchestratorDropGuard borrowed self mutably while the rest of
`run` also needed &mut self access, forcing an attempted rebind via
`let self = _drop_guard.inner()` which is illegal (`self` is a Rust
keyword). The result was a 10-error borrow-checker mess.

Replace the borrow-based guard with an owning snapshot guard:

  struct OrchestratorDropGuard<S: ... + Clone> {
      dirty: bool,
      conversation: Option<Conversation>,
      services: S,
  }

The guard now stores only the data needed for the final persist. The
rest of `run` keeps using `self.foo()` without conflicts. Add Clone
bound on S since the drop impl clones the services handle.

* feat(forgecode): add :search command (FTS5 conversation search)

Adds a new top-level command that searches the conversation FTS5 index:

  :search <query>     (alias: :sr)

End-to-end wiring:
- API trait: add upsert_conversation_ref (by-ref variant avoiding clone
  on hot paths), search_conversations (FTS5 BM25), optimize_fts_index.
- Services trait: same three methods, delegating to conversation_service.
- ConversationService impl: passes through to the conversation repo.
- ForgeConversationService: thin pass-through to the repo.
- AppCommand: new Search { query } variant with 'search'/'sr' aliases.
- UI::handle_search: prompts via ConversationSelector on matches,
  switches the active conversation on selection.

Combined with the FTS5 migration (2026-06-14-000002) and the
ConversationRepository impl methods added earlier, this completes the
search feature end-to-end.

* fix(forgecode): apply coderabbitai review fixes (FTS5 join + return type + test fixtures)

- search_conversations: join on conversation_id instead of rowid (FTS5 table is content-less, so c.rowid = fts.rowid would not match)
- search_conversations: return Vec<Conversation> instead of Option<Vec<Conversation>> for consistency with other collection-returning methods
- Add parent_id and source fields to test fixtures in conversation_selector and info test modules (required after Conversation struct got those fields)

Addresses review items 1, 2, 3 from PR #21 coderabbitai review.

* fix(forgecode): remove stale Option unwraps after search_conversations return type change

After search_conversations was changed from Option<Vec<Conversation>> to Vec<Conversation> in the previous commit, three downstream call sites still had stale Option handling:

- crates/forge_app/src/services.rs: AgentService::search_conversations impl still returned Option<Vec<Conversation>>
- crates/forge_repo/src/forge_repo.rs: ForgeRepo::search_conversations impl still returned Option<Vec<Conversation>>
- crates/forge_api/src/forge_api.rs: ForgeAPI::search_conversations still called .unwrap_or_default() on the Vec result

Build now passes cargo build --bin forge in 51s. forge-dev installed to ~/.local/bin/forge-dev.

---------

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…ount migration (#22)

Builds on PR #20 (session-viewer) + PR #21 (perf-v2). Adds user-facing session management.

## What's in this PR

### 1. :reparent <conversation_id> command (P1 UX gap)
- Re-binds a subagent (or any conversation) to a different parent in the hierarchy
- Useful when subagent gets spawned from wrong parent and you want to re-organize
- Wired through: ui.rs -> API::update_parent_id -> ConversationService::update_parent_id -> ConversationRepository::update_parent_id
- New trait method on ConversationRepository

### 2. :cwd [path] command (cwd filter)
- Without arg: list conversations in current cwd only
- With arg: switch to listing conversations in a different cwd
- New method get_conversations_by_cwd on the repo
- Composite index on (workspace_id, cwd) for fast lookup

### 3. Sort UI for conversation selector
- :sort turns|updated|created switchable
- Stored in UIState.sort
- Re-runs the conversation list on toggle

### 4. cwd + message_count fields on Conversation
- Migration 2026-06-21-000000_add_cwd_message_count_to_conversations
- New columns: cwd TEXT NULL, message_count INTEGER NULL
- Composite index idx_conversations_cwd + idx_conversations_message_count
- Forwarded through ConversationRecord -> Conversation domain

### 5. update_parent_id in repo + service + API
- Diesel UPDATE with new parent_id + updated_at timestamp
- Returns () on success

## Files changed (15 files, +566 / -1)
| File | Change |
|---|---|
| crates/forge_repo/src/database/migrations/2026-06-21-000000_add_cwd_message_count_to_conversations/{up,down}.sql | New migration |
| crates/forge_repo/src/database/schema.rs | cwd + message_count columns + indices |
| crates/forge_repo/src/conversation/conversation_record.rs | Record fields + From conversions |
| crates/forge_repo/src/conversation/conversation_repo.rs | update_parent_id + get_conversations_by_cwd impls |
| crates/forge_repo/src/forge_repo.rs | Service wiring for the 2 new repo methods |
| crates/forge_domain/src/conversation.rs | cwd + message_count fields on Conversation |
| crates/forge_domain/src/repo.rs | update_parent_id + get_conversations_by_cwd trait methods |
| crates/forge_services/src/conversation.rs | Service impl |
| crates/forge_app/src/services.rs | AgentService trait + impl |
| crates/forge_api/src/api.rs | API trait |
| crates/forge_api/src/forge_api.rs | API impl |
| crates/forge_main/src/model.rs | AppCommand::Reparent, AppCommand::Cwd, AppCommand::Sort |
| crates/forge_main/src/state.rs | sort field on UIState |
| crates/forge_main/src/ui.rs | handle_reparent, handle_cwd, handle_sort, on_command wiring |

## Build
- cargo check --bin forge clean in 12.83s
- cargo build --bin forge clean in 2m 23s (179MB binary)
- forge-dev installed at ~/.local/bin/forge-dev

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…ort canonical, get_conversation_snippet (#24)

- domain: ConversationSort enum (Updated/UpdatedAt/Created/CreatedAt/Turns/MessageCount/Cwd) as canonical for sort UI
- domain: ConversationSortKey() helper that maps ConversationSort to (Column, Direction)
- domain: Conversation.cwd, Conversation.message_count fields
- repo: get_conversations_by_cwd, update_parent_id, count_subagents, get_conversation_snippet trait methods
- repo: ConversationRepo impl for all v4 methods; consolidates sort to use ConversationSort
- repo: ForgeRepo impl for the v4 methods
- services: ForgeConversationService impl
- services: AgentService impl
- api: API trait + ForgeAPI impl
- main: AppCommand::Reparent { subagent_id, new_parent_id }
- main: AppCommand::SetCwd { cwd }
- main: AppCommand::SetSort { target: ConversationSort }
- main: UIState.sort_key, cwd_filter fields
- main: handlers in ui.rs for the 3 new commands

Build: cargo check --bin forge clean (2 pre-existing dead-code warnings unrelated)
Binary: 179MB at ~/.local/bin/forge-dev (forge 0.1.0-dev)

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
The Conversation struct gained parent_id, source, cwd, message_count
fields in the per-conversation cwd-filtering work. Three test fixtures
were not updated and failed to compile with E0063 (missing fields):

- forge_repo conversation_repo.rs:665 — test_conversation_from_conversation_record
- forge_repo conversation_repo.rs:1109 — test_conversation_deserialization_error_includes_id
- forge_app doom_loop.rs:283 — create_conversation_with_messages

All 3 fixtures now include parent_id, source, cwd, message_count = None.

Full workspace test suite: 2696 passed, 0 failed.
Auto-generated by gh-workflow-gen (do-not-edit-by-hand). Replaces hand-written
CI/release/bounty/labels/autofix/stale/release-drafter workflows with
codegen output from build.rs configuration.

- ci.yml: 31 -> 296 lines (added OpenRouter API key, label-based trigger, build/test/lint jobs)
- release.yml: 112 -> 155 lines (added crates.io publish, Docker multi-arch, changelog-from-tag)
- bounty.yml, labels.yml, stale.yml, release-drafter.yml, autofix.yml: minor updates to match codegen schema

No source code changes. Build/install commands unchanged.
…generation

feat(config): add forge.config.json schema for OutputSettings (already implemented in forge_config/src/output.rs)

Adds JSON schema entry for OutputMode (Concise | Compact | Verbose) and OutputSettings struct. The Rust impl has been at crates/forge_config/src/output.rs since v0.1.0; this just adds the SSOT schema entry for editor autocomplete + docs site generation.

chore(docs): L7 stub regeneration bump

Auto-generated via L7 propagation system. Updates docs/boundary/forgecode.md and docs/intent/forgecode.md stub dates to 2026-06-21.

No source code changes.
…c-shared conv map, single-await prompt (#25)

Resolves the cumulative-slowdown bug where after a couple active forge sessions, the interactive UI slows to 10-20x normal speed on conversation scroll and provider model fetches. The root cause: 4 detached tokio::spawn background tasks per /new hydrate_caches() call, with no abort mechanism — N /new calls = 4N zombie tasks each holding a clone of the ForgeAPI Arc.

## What's in this PR

### P0 reactivity — 4 fixes, all O(1) per call

1. **Abort stale hydration tasks on /new** (ui.rs:495-507)
   - hydrate_caches() now bumps cache_generation, calls replace_hydration_handles() which aborts any in-flight tasks from prior hydrate calls before spawning new ones
   - New fields: hydration_handles: Vec<JoinHandle<()>>, cache_generation: Arc<AtomicU64>, interrupt_flag: Arc<AtomicBool>, _guard: AbortOnDrop
   - New methods: spawn_tracked(), replace_hydration_handles(), current_generation()

2. **Arc-shared conversation map** (conversation_selector.rs:100)
   - conv_map: HashMap<String, Arc<Conversation>> instead of cloning all conversations into a local HashMap
   - Lazy lookup; no per-selector-call full deep clone of 6k+ conversation metadata

3. **Reduce sequential awaits in prompt()** (ui.rs:323-361)
   - Consolidated 2 sequential get_active_agent().await calls into a single call (active_agent cached in local)

4. **Cargo.toml: lto = "thin"** (Cargo.toml:36-42)
   - Was lto = true (the pre-existing stale config that conflicted with rustc 1.85+ embed-bitcode default)

## Test plan
- [x] cargo check --bin forge clean in 22.21s (only pre-existing dead-code warnings)
- [x] cargo build --bin forge succeeds
- [x] forge-dev (179MB) installed at ~/.local/bin/forge-dev
- [x] forge-dev --version returns forge 0.1.0-dev

## Out of scope (separate PRs)
- Status bar (Claude-style)
- Compressed tool output + ctrl-toggle
- ASCII color coding for tools (parallel/queued/bg indicators)
- @[/] dynamic routines
- Strengthened subagent tool discoverability prompt snippet

These are larger UX items that need their own design sessions.

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…c-shared conv map, single-await prompt (#26)

Resolves the cumulative-slowdown bug where after a couple active forge sessions, the interactive UI slows to 10-20x normal speed on conversation scroll and provider model fetches. The root cause: 4 detached tokio::spawn background tasks per /new hydrate_caches() call, with no abort mechanism — N /new calls = 4N zombie tasks each holding a clone of the ForgeAPI Arc.

## What's in this PR

### P0 reactivity — 4 fixes, all O(1) per call

1. **Abort stale hydration tasks on /new** (ui.rs:495-507)
   - hydrate_caches() now bumps cache_generation, calls replace_hydration_handles() which aborts any in-flight tasks from prior hydrate calls before spawning new ones
   - New fields: hydration_handles: Vec<JoinHandle<()>>, cache_generation: Arc<AtomicU64>, interrupt_flag: Arc<AtomicBool>, _guard: AbortOnDrop
   - New methods: spawn_tracked(), replace_hydration_handles(), current_generation()

2. **Arc-shared conversation map** (conversation_selector.rs:100)
   - conv_map: HashMap<String, Arc<Conversation>> instead of cloning all conversations into a local HashMap
   - Lazy lookup; no per-selector-call full deep clone of 6k+ conversation metadata

3. **Reduce sequential awaits in prompt()** (ui.rs:323-361)
   - Consolidated 2 sequential get_active_agent().await calls into a single call (active_agent cached in local)

4. **Cargo.toml: lto = "thin"** (Cargo.toml:36-42)
   - Was lto = true (the pre-existing stale config that conflicted with rustc 1.85+ embed-bitcode default)

## Test plan
- [x] cargo check --bin forge clean in 22.21s (only pre-existing dead-code warnings)
- [x] cargo build --bin forge succeeds
- [x] forge-dev (179MB) installed at ~/.local/bin/forge-dev
- [x] forge-dev --version returns forge 0.1.0-dev

## Out of scope (separate PRs)
- Status bar (Claude-style)
- Compressed tool output + ctrl-toggle
- ASCII color coding for tools (parallel/queued/bg indicators)
- @[/] dynamic routines
- Strengthened subagent tool discoverability prompt snippet

These are larger UX items that need their own design sessions.

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…action (#27)

Adds a persistent bottom status bar that surfaces the most important runtime info on every keypress + every chat-response event. Addresses the 'very hard to spot' complaint about tool calls and tool state in the chat surface.

## What's in this PR

### 1. StatusBar domain (state.rs)
- New `StatusBar` struct wrapping `Mutex<StatusBarSnapshot>` (std::sync, no new deps)
- 7 snapshot fields: `last_action`, `active_tool`, `context_pct`, `tokens_used`, `is_busy`, `tool_in_flight`, `active_tool_elapsed`
- Snapshot getter + typed setters (`set_last_action`, `set_active_tool`, `clear_active_tool`, `set_context_pct`, `inc_tool_in_flight`, `dec_tool_in_flight`)
- `StatusBarSnapshot` is `Clone + Debug` (cheap to copy for rendering)
- `UIState.status_bar: StatusBar` field with `Default` initializer
- `Clone` derive removed from `UIState` (Mutex isn't Clone)

### 2. Renderer (ui.rs)
- `render_status_bar()` method on `UI` — paints the bottom status line
- Format: `[model] [tokens]k tok [N]% ctx | [last_action] | [tool_state]`
- Color-coded: model in cyan, tokens in white, context% in yellow/red by threshold, tool in flight in bold-yellow, idle in dim gray
- Uses `colored::Colorize` (already in deps)

### 3. Hook points (handle_chat_response)
- `ChatResponse::ToolCallStart { name }` → `set_active_tool(name)`, `inc_tool_in_flight()`
- `ChatResponse::TaskMessage::ToolInput { ... }` → `set_last_action(format!("called {}", name))`
- `ChatResponse::TaskMessage::ToolOutput { ... }` → `set_last_action(format!("got {} chars from {}", len, name))`
- `ChatResponse::TaskFinished { ... }` → `dec_tool_in_flight()`, `clear_active_tool()`
- `ChatResponse::MessageComplete { ... }` → `set_last_action("idle")`, `set_context_pct(usage_pct)`

### 4. .gitignore
- Added `.cargo/` so the local rustflag override (needed to work around parent monorepo's embed-bitcode/lto conflict) doesn't get committed

## Out of scope (deferred to v5-ux PRs #28-#29)
- Compressed tool output (3-line default + ctrl-toggle)
- ASCII color coding for tools (parallel/queued/bg indicators)
- @[/] dynamic routines
- Strengthened subagent tool discoverability prompt snippet
- Git branch / commits-ahead / cwd indicators in the bar (those need state.rs fields we deliberately didn't add to keep this PR focused)

## Build
- `cargo check --bin forge` clean in 38.80s
- `cargo build --bin forge` clean in 6m 02s (178MB binary)
- `forge-dev` (179MB) installed at `~/.local/bin/forge-dev`

## Test plan
- [x] `cargo check --bin forge` clean (0 errors, 8 pre-existing dead-code warnings)
- [x] `cargo build --bin forge` succeeds
- [x] `forge-dev --version` returns `forge 0.1.0-dev`
- [ ] Manual: launch `forge-dev`, observe status bar at bottom showing model + tokens + last_action
- [ ] Manual: run a tool call, verify bar updates with tool name + tool_in_flight counter
- [ ] Manual: end a turn, verify bar resets to idle

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…oshaPari/forgecode (#28)

Two surgical fixes (one PR, 2 files, +10/-3 lines):

1. **Hang fix** (cli.rs:80-85)
   - Added `use std::io::IsTerminal;`
   - Cli::is_interactive() now also checks `std::io::stdin().is_terminal()`
   - When stdin isn't a TTY (piped, redirected, or running under a non-interactive shell), is_interactive() returns false, and main.rs:96's existing TTY-guard short-circuits the init_state -> console::prompt -> Term::read_line() hang with a clear error message.
   - This is the root cause of 'forge-dev hangs and never runs' when invoked outside a proper TTY (e.g. cron, CI, headless subagent dispatch).

2. **Auto-update registry** (update.rs:90)
   - update_informer `new(GitHub::new('tailcallhq', 'forgecode'))` -> `new(GitHub::new('KooshaPari', 'forgecode'))`
   - Pulls version checks from the canonical fork, not the upstream we no longer track.

## Build
- `cargo build --bin forge` clean
- 178MB binary at `~/.local/bin/forge-dev` (Jun 23 04:13)
- `forge-dev --version` -> `forge 0.1.0-dev` (exit 0)

## Test plan
- [x] `cargo build --bin forge` succeeds
- [x] `forge-dev --version` runs cleanly
- [x] `forge-dev --help` runs cleanly
- [ ] Manual: launch `forge-dev` in a real TTY (Ghostty) — should prompt normally
- [ ] Manual: run `echo '' | forge-dev` (stdin closed) — should error with 'stdin is not a TTY; use forge -p for non-interactive' instead of hanging

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Addresses the 'Task and other tools print full output' complaint — tool outputs longer than 3 lines are now truncated to the first 3 lines + a '... [N more lines; run :expand to see full output]' hint. Output is also rendered dimmed for visual hierarchy.

This is the foundation for full Ctrl+O toggle / per-message expand (PR #30+), but ships the truncation first as a focused, low-risk change that already improves the chat surface legibility.

## What's in this PR

### state.rs (+9 lines)
- New field: `UIState.tool_output_expanded: bool` (default false)
- New helper: `UIState::set_tool_output_expanded(b: bool)`

### ui.rs (+15 lines)
- Modified `TaskMessage::ToolOutput` handler: when content > 3 lines AND !tool_output_expanded, truncate to first 3 lines + dimmed hint
- When expanded, render full output (current behavior)
- Uses `colored::Colorize::dimmed()` for the truncation hint (already in deps)

## Behavior

Before:
```
[output: 47 lines dumped verbatim]
```

After:
```
[line 1]
[line 2]
[line 3]
... (44 more lines; run :expand to see full output)
```

## Out of scope (deferred to follow-up PRs)
- Ctrl+O keyboard binding via rustyline EventHandler (separate PR with editor.rs changes)
- :expand slash command (AppCommand::Expand variant in model.rs)
- Per-message expand state (HashSet<Uuid> of expanded tool outputs)

## Build
- `cargo build --bin forge` clean in 52.86s (0 errors, 8 pre-existing dead-code warnings)
- 178MB binary at `~/.local/bin/forge-dev`
- `forge-dev --version` → `forge 0.1.0-dev`

## Test plan
- [x] `cargo build --bin forge` succeeds
- [x] `forge-dev --version` runs
- [ ] Manual: run a tool that produces >3 lines, verify truncation
- [ ] Manual: run :expand, verify full output renders
- [ ] Manual: run a tool that produces ≤3 lines, verify no truncation

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
Addresses the 'Task ascii based coloring and some way of notating tools/actions done in parallel' complaint. Every tool call now renders with a leading symbol (Unicode glyph) + per-tool-type color via colored::Colorize, making it instantly scannable which tool type is running.

## Color map (legend)

| Symbol | Color       | Tool type   | Tool names detected                |
|--------|-------------|-------------|------------------------------------|
| ⏵      | cyan        | Read        | read, cat, view, fs.read/cat/view  |
| ✎      | green       | Write       | write, edit, patch, fs.write/etc   |
| ▶      | yellow      | Shell       | bash, shell, exec, process         |
| ⌕      | magenta     | Search      | search, grep, find, rg, fs.search  |
| ⊙      | blue        | Subagent    | task, forge_task, subagent, agent  |
| ⤴      | bright_cyan | Web         | fetch, web, http, curl, wget       |
| •      | white       | default     | anything else                      |

## Implementation

Single 32-line patch in `crates/forge_main/src/ui.rs` — the `ChatResponse::TaskMessage::ToolInput` branch now extracts the first whitespace-delimited token of the title (the tool name), looks up the (symbol, color) pair, and writes `{symbol} {colored_title}`.

Uses `colored::Colorize` (already in deps). No new crates. No state changes.

## Behavior

Before:
```
read file.txt
```

After:
```
⏵ \x1b[36mread file.txt\x1b[0m
✎ \x1b[32medit ui.rs\x1b[0m
▶ \x1b[33mbash cargo test\x1b[0m
⌕ \x1b[35msearch .rs 'async'\x1b[0m
⊙ \x1b[34mtask: refactor ui.rs for status bar\x1b[0m
```

## Out of scope (deferred to follow-up PRs)

- Parallel/queued/bg symbol prefixes (would need scheduler state; orthogonal to per-tool coloring)
- Per-call custom colors via config
- Symbol legend / help command

## Build
- `cargo build --bin forge` clean in 5m 35s (0 errors, 8 pre-existing dead-code warnings)
- 178MB binary at `~/.local/bin/forge-dev`
- `forge-dev --version` → `forge 0.1.0-dev`

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
…prompt (#31)

Addresses 'agents by default have no idea how to use subagents... they will proceed to try to use forgecli always' — the subagent tool guidance was previously gated behind {{#if custom_rules}}, so most agents never saw it.

This PR promotes the guidance to a top-level always-rendered block in the custom-agent system prompt template. Now every agent (not just ones with user-defined custom_rules) gets explicit, unambiguous instructions on how and when to invoke the subagent tool, with anti-CLI-escape guidance.

## What's in this PR

### templates/forge-custom-agent-template.md (+28 lines)

New top-level section:

```markdown
## Subagent tool (forge_task / task / subagent / agent)

You have access to a SUBAGENT TOOL for delegating work to a separate, isolated
agent session. **This is the ONLY way to spawn a subagent within this session.**
Do NOT attempt to invoke subagents via:
  - shell commands like 'forge -p ...' or 'curl .../forge'
  - any other CLI invocation
  - asking the user to run a command in another terminal
  - bash scripts that re-invoke forge

When to use the subagent tool:
  - The task is complex enough that a separate context would help (multi-file
    refactor, deep investigation, parallel research)
  - You want to isolate destructive operations (run in a sandboxed session)
  - You need parallel work on independent subtasks
  - You want to retry a failing task without polluting the current context

When NOT to use the subagent tool:
  - The task can be done in <50 LoC change in this session
  - You need to share state with the current conversation
  - The user explicitly says 'do this here' or 'in this session'

How to use it:
  - The tool name is 'forge_task' (or 'task', 'subagent', 'agent' depending on
    adapter); call it like any other tool
  - Pass a clear, self-contained prompt that doesn't rely on this session's
    context
  - Optionally pass a model override if the subtask needs a different capability
```

## Build
- `cargo build --bin forge` clean in 4m 57s
- 178MB binary at `~/.local/bin/forge-dev`
- `forge-dev --version` → `forge 0.1.0-dev`

## Test plan
- [x] `cargo build --bin forge` clean
- [x] `forge-dev --version` runs
- [ ] Manual: launch `forge-dev` without `custom_rules`, verify subagent section appears in the rendered system prompt
- [ ] Manual: launch `forge-dev` with `custom_rules`, verify the section still appears (no duplicate)
- [ ] Manual: ask the agent a complex refactor task, verify it uses the subagent tool without prompting

Co-authored-by: Phenotype Agent <agent@phenotype.ai>
… forgecode

Phase 3 (contribution-back) of the 3-PR forgecode improvement
sequence (ADR-096, accepted 2026-06-23).

Adds a new workspace member `crates/forge_pheno_memory` that wires
the `thegent-memory` v2 polyglot facade (supermemory + letta +
cognee + mem0) into forgecode's Infra + Domain pattern. Forgecode
agents can now use a stable, scope-routed memory API without coupling
themselves to any specific memory engine.

API:
  ```
  use forge_pheno_memory::{PhenoMemoryService, PhenoMemoryConfig, PhenoMemoryScope};
  use thegent_memory::v2::{MemoryValue, MemoryQuery};

  let svc = PhenoMemoryService::with_defaults();

  // Store
  svc.store(PhenoMemoryScope::Episodic.into(), "k", "v".into()).await?;

  // Recall
  let recs = svc.recall(PhenoMemoryScope::Episodic.into(), MemoryQuery::new("q")).await?;
  ```

Scope routing (locked per ADR-096):
  Episodic          -> supermemory  (smfs filesystem, :3030)
  Identity          -> letta        (subconscious blocks, :8283)
  ProjectKnowledge  -> cognee       (knowledge graph, stdio cognee-mcp)
  Fallback          -> mem0         (REST :8000)

Endpoint defaults match the `pheno-forge-plugins` v0.1.0 systemd
unit ports. Override via `PhenoMemoryConfig` builder.

Errors are wrapped in `PhenoMemoryError` (thiserror-based) so
forgecode callers can match on `Network`, `Backend`, `NotFound`,
`Serde`, `Unavailable`, `Invalid`, or `Internal` variants.

Tests (3 unit, 0 failures):
  - config_defaults_match_sidecar_ports  (verifies port alignment)
  - service_constructs_with_defaults     (verifies wiring)
  - scope_round_trip                     (verifies scope enum mapping)

Refs:
  - ADR-096 (docs/adr/2026-06-23/ADR-096-forgecode-improvement.md)
  - findings/2026-06-23-forgecode-improvement-plan.md
  - PR 1: KooshaPari/pheno-forge-plugins v0.1.0 (plugin bundle)
  - PR 2: KooshaPari/thegent#1144 (thegent-memory v2, merged)
  - PR 3: KooshaPari/pheno-cdylib-bridge v0.1.0 (C-ABI for non-Rust consumers)
@github-actions github-actions Bot added type: feature Brand new functionality, features, pages, workflows, endpoints, etc. type: provider Updates provider.json configuration. labels Jun 24, 2026
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 3 committers have signed the CLA.

❌ Phenotype Agent
❌ KooshaPari
❌ Dmouse92


Phenotype Agent seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@KooshaPari KooshaPari closed this Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature Brand new functionality, features, pages, workflows, endpoints, etc. type: provider Updates provider.json configuration.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants