pub struct CreatorInfo {
pub client: String,
pub version: String,
pub source: CreatorSource,
pub cwd: Option<String>,
pub workstream: Option<String>,
}Expand description
Value type describing the writer of a drawer.
Why: each write path builds one of these and merges the rendered tags
into the caller-supplied tag list before persisting. Keeping the
rendering centralised guarantees every write produces tags in the
same order with the same prefixes, so curl + grep workflows stay
stable.
What: holds an owned client name, an owned version string, the source
enum, and an optional cwd. into_tags() consumes the value and
returns the rendered tag list.
Test: creator_info_renders_all_fields,
creator_info_omits_cwd_when_absent.
Fields§
§client: String§version: String§source: CreatorSource§cwd: Option<String>§workstream: Option<String>Implementations§
Source§impl CreatorInfo
impl CreatorInfo
Sourcepub fn new_self(client: impl Into<String>, source: CreatorSource) -> Self
pub fn new_self(client: impl Into<String>, source: CreatorSource) -> Self
Build a CreatorInfo with the supplied client + source, defaulting
the version to this crate’s CARGO_PKG_VERSION and the cwd/workstream
to whatever this process has at construction time.
§⚠️ Daemon-vs-caller hazard — read before calling this from a shared
§server dispatch path
new_self resolves identity from the CALLING PROCESS’ own
environment (std::env::current_dir(), TM_WORKSTREAM_NAME). That is
correct only when the process constructing the CreatorInfo genuinely
is the writer — a standalone CLI invocation, or a hook that runs
once per invocation in the caller’s own process tree. It is WRONG
for any handler that runs inside trusty-memory’s shared HTTP/MCP
daemon (every crate::tools::* handler, reached via POST /rpc from
the stdio bridge or any other remote caller): the daemon is ONE
long-lived process serving MANY concurrently-attached sessions, so
new_self there would resolve the daemon’s own cwd/env — the same
value for every caller — producing cross-session mis-attribution (the
exact bug DOC-53’s caller-supplied design (new_for_caller) exists to
prevent). Use CreatorInfo::new_for_caller for any write reached
through the shared daemon’s dispatch surface; reserve new_self for
code that truly executes in the writer’s own process.
What: client.into() + env!("CARGO_PKG_VERSION").into() +
std::env::current_dir().ok().map(...) + [resolve_own_workstream_name].
Test: creator_info_self_populates_version_and_cwd.
Sourcepub fn new_for_caller(
client: impl Into<String>,
source: CreatorSource,
caller_cwd: Option<&str>,
caller_workstream: Option<&str>,
) -> Self
pub fn new_for_caller( client: impl Into<String>, source: CreatorSource, caller_cwd: Option<&str>, caller_workstream: Option<&str>, ) -> Self
Build a CreatorInfo for a write reached through the shared daemon’s
dispatch surface (MCP tools/call/direct-method, or HTTP), using ONLY
caller-supplied context — never the daemon process’ own env/cwd.
Why (critical fix, DOC-53 §4.3): the daemon serves every
concurrently-attached session from ONE process. new_self‘s
std::env::current_dir()/TM_WORKSTREAM_NAME reads would resolve the
daemon’s identity, identically for every caller — this is the
constructor that instead trusts only what the specific request
carried, mirroring the existing args["cwd"] precedent in
tools::palace_ops::handle_palace_create (caller value wins; no
silent daemon-identity fallback for either field).
What: cwd is caller_cwd verbatim (empty-string treated as absent,
never re-derived from the daemon’s own cwd). workstream prefers
caller_workstream when it passes is_valid_workstream_name — an
explicit-but-invalid value resolves to None, NOT a silent fallback
to the cwd heuristic (same “explicit-but-bad is None” rule
[resolve_own_workstream_name] already applies to the env var) —
else falls back to resolve_workstream_name against caller_cwd.
Neither field ever touches this process’ own env or cwd.
Test: new_for_caller_prefers_explicit_workstream_over_cwd,
new_for_caller_falls_back_to_cwd_when_workstream_absent,
new_for_caller_omits_workstream_when_neither_resolvable,
new_for_caller_invalid_explicit_workstream_returns_none_not_cwd_fallback.
Render the rendered tag strings in stable order.
Why: stable order keeps tests deterministic and gives operators a
predictable layout when they grep through palaces with jq.
What: [client, version, source, cwd?, creator:workstream?, ws?].
cwd and the workstream pair are each omitted when absent rather
than rendered with an empty/placeholder value, so downstream
consumers can distinguish “writer didn’t share this” from “writer’s
value was literally empty” (DOC-53 §4.1 — no placeholder ever).
Test: creator_info_renders_all_fields,
creator_info_omits_cwd_when_absent,
creator_info_renders_workstream_tags_when_resolvable,
creator_info_omits_workstream_tags_when_absent_or_invalid.
Sourcepub fn merge_into(self, dst: &mut Vec<String>)
pub fn merge_into(self, dst: &mut Vec<String>)
Render the tags and append them to an existing tag list.
Why: write-path call sites already hold a Vec<String> of
user-supplied tags; merging in place avoids an allocation and
preserves the caller’s ordering.
What: pushes each rendered tag onto dst. Does not deduplicate —
caller is expected to pass a freshly-built or de-duplicated list.
Test: merge_into_appends_creator_tags.
Sourcepub fn merge_into_deduped(self, dst: &mut Vec<String>)
pub fn merge_into_deduped(self, dst: &mut Vec<String>)
Render the tags and append them to an existing tag list, skipping any
tag already present verbatim in dst.
Why (MEDIUM 1, DOC-53 §3.1): a hand-written claim drawer already
carries ws:<name> in its caller-supplied tags by convention (the
claim-drawer shape); merge_into would then append a second,
identical ws:<name> (and, since the caller-supplied workstream and
the auto-stamped one are the same value, an identical
creator:workstream=<name> too if the caller happened to write that
literal tag). Use this instead of merge_into for any write path
where the caller’s own tags may already overlap the auto-stamped
namespace — currently [crate::tools::helpers::attach_mcp_attribution].
What: exact-string dedup only (not case-insensitive, not prefix-aware)
— a tag is skipped iff it already appears verbatim in dst.
Test: merge_into_deduped_skips_tags_already_present,
merge_into_deduped_appends_when_no_overlap.
Trait Implementations§
Source§impl Clone for CreatorInfo
impl Clone for CreatorInfo
Source§fn clone(&self) -> CreatorInfo
fn clone(&self) -> CreatorInfo
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for CreatorInfo
impl Debug for CreatorInfo
impl Eq for CreatorInfo
Source§impl PartialEq for CreatorInfo
impl PartialEq for CreatorInfo
impl StructuralPartialEq for CreatorInfo
Auto Trait Implementations§
impl Freeze for CreatorInfo
impl RefUnwindSafe for CreatorInfo
impl Send for CreatorInfo
impl Sync for CreatorInfo
impl Unpin for CreatorInfo
impl UnsafeUnpin for CreatorInfo
impl UnwindSafe for CreatorInfo
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more