shared + core + cli + web: named humans on every promote (R19) - #105
Conversation
A team needs to know whose judgment a decided fact carries. Every
promote-to-decided now records the human behind it.
- Migration 0018: additive nullable decided_by on entity/decision/
question/goal. Existing decided facts read back with a null decider;
nothing is backfilled.
- Identity precedence (resolveDecider): explicit --as flag > MARROW_USER
> OS login name > undefined (a solo tool that never set an identity
leaves the fact unattributed rather than guessing; root/node container
users are treated as unset).
- Written once at promotion in store.promoteToDecided and the authorGoal
insert, carried on confidence.decidedBy through the read paths.
- Surfaced: CLI formatNode ('1.00 human (priya)'), web SourcePanel
('priya stands behind this'), MCP trace via the confidence object.
CLI answer/accept/goal author gained --as.
Sacred rule held: decided_by is metadata ABOUT the promote event, never
about what may be decided or by what path. No MCP tool can set it (the
propose_node test asserts a proposed node carries no decider), because
no MCP tool can promote at all. It also stays OUT of the token-scoped
prepare_task brief and the benchmark measurement, so the agent's token
cost and the CI-gated 2.9x/1.5x/recall-1.0 numbers are unchanged, and a
new test proves the benchmark count is identical regardless of MARROW_USER.
Live-proven all three precedence tiers (--as priya, MARROW_USER=marco,
OS-user fallback) and the readback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
marrow/packages/core/src/marrow.ts
Lines 2479 to 2481 in 4a14b0d
When a caller has an explicit human identity for a batch answer (for example a web-session identity), this path drops it before calling answer(), so every promoted node in /api/answer-batch falls back to MARROW_USER or the OS user. In hosted/server contexts where that fallback is unset or a shared process user, batch-promoted facts end up unattributed or attributed to the wrong human, unlike single /api/answer promotions.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
# Conflicts: # packages/core/src/index.ts # packages/core/src/marrow.ts
What (roadmap arc 5, R19) — PAUSED FOR HUMAN REVIEW per the loop rules (decide-boundary metadata + a migration)
A team needs to know whose judgment a decided fact carries. Every promote-to-decided now records the human behind it.
decided_byon entity/decision/question/goal. Existing decided facts read back with a null decider; nothing is backfilled.resolveDecider): explicit--asflag →MARROW_USER→ OS login name → undefined (a solo tool that never set an identity leaves the fact unattributed rather than guessing; containerroot/nodeusers are treated as unset).store.promoteToDecidedand theauthorGoalinsert, carried onconfidence.decidedBy.formatNode(1.00 human (priya)), web SourcePanel (priya stands behind this), MCPtrace_to_sourcevia the confidence object. CLIanswer/accept/goal authorgained--as.The sacred-rule proof
decided_byis metadata about the promote event, never about what may be decided or by what path.append_evidence/propose_nodetest now asserts a proposed node carries no decider.prepare_taskbrief (stripped inbriefNode) and out of the benchmark measurement (measureJson), so the agent's token cost is unchanged and the CI-gated 2.9x / 1.5x / recall 1.0 numbers hold. A new benchmark test proves the token count is identical regardless ofMARROW_USER(the decider would otherwise leak the env's OS user and drift the report by machine).Live proof
All three precedence tiers:
--as priya→1.00 human (priya);MARROW_USER=marco→(marco); OS-user fallback →(elie). Readback viagoals/decisions.Battery
typecheck / lint clean, root test
ℹ fail 0, smoke:packed ok, benchmark drift clean (deterministic fields unchanged; the decider never enters the measured path).Sacred rules
The only paths to decided remain
answerandauthorGoal(the human path);decided_bywrites only alongsideconfidence_source = 'human'. Migration is additive and idempotent. No new decide path, no agent decide path.🤖 Generated with Claude Code