Skip to main content
HUMΛN
Architecture
Architecture

HumanOS learning, tuning, and rollback — policy boundary

HUMΛN Team··7 min

Getting better without getting sneaky

Learning is the feature users want and regulators fear — because most stacks implement it as invisible drift. Our answer is governed adaptation: P2 for feedback events, P10 for tuning actions, P8/P9 for what may change without humans, and learning_requires_approval surfaced on policy/effective.

Append-only honesty

POST /v1/humanos/feedback/events is intentionally boring: who, scope, payload type, target, timestamp. Boring logs win audits. GET /v1/humanos/feedback/events lists them with cursor pagination for operators and Signals-style workflows. POST /v1/humanos/tuning/actions records “more like this,” “mute,” “digest” — the human-scale verbs that should never be reimplemented as hidden flags in business logic.

Learning proposals (governed adaptation): GET /v1/humanos/policy/effective includes a learning object (memory scopes allowed, feedback types allowed, org flags). POST /v1/humanos/learning/proposals creates drafts (optional expires_at for time-boxed drafts); GET /v1/humanos/learning/proposals lists them with cursor pagination; PATCH /v1/humanos/learning/proposals/:proposal_id updates status; POST /v1/humanos/learning/proposals/:proposal_id/apply materializes an approved proposal into humanos_tuning_actions (optional proposal.payload.tuning_expires_at or inherited proposal expires_at on the tuning row). POST /v1/humanos/learning/infer/v0 produces deterministic draft proposals from recent feedback (rules only — no ML in v0). GET /v1/humanos/tuning/compare-default folds org tuning against package defaults for drift narratives. Expired draft/pending proposals are rolled back to rolled_back by a background decay tick (configurable via HUMANOS_LEARNING_DECAY_POLL_MS; set HUMANOS_LEARNING_DECAY_WORKER=disabled to turn off). App authors use HumanClient from @human/client-sdk (client.humanos.getPolicyEffective(), listFeedbackEvents, createLearningProposal, applyLearningProposal, …) or Human from @human/sdk (client.humanosLearning — same REST paths).

The line between preference and policy

Preferences may adapt; policy stays explicit. If a durable mutation would weaken review thresholds, the policy object must allow it — or the merge is blocked. That is how we keep “the model decided” from becoming operational doctrine.

Use cases

  1. Given a user thumbs-up on a summary, when the event is stored, then provenance can cite the feedback id later in an explainability bundle.
  2. Given an org-wide tuning proposal, when learning_requires_approval is true, then approval queue gates the change — no silent rollout.
  3. Given a rollback drill, when preferences revert, then effective behavior returns with traceable history.

Trust expectation: Users feel this got better for me — and when they open the hood, I can see what changed and undo it.


Extended narrative — fold, don’t fudge

Tuning is append-only because audit beats edit. Effective preferences are computed by folding actions: digest mode changes, mute/unmute, and revert_tuning as the big red reset. Tests prove rollback clears digest + mutes — not because we trust comments, but because we trust the reducer.

Org scope is where dark patterns hide, so when learning_requires_approval is true, POST /v1/humanos/tuning/actions demands an approval_request_id for org/team scopes (optional expires_at on the action). Flip the flag false in policy only when founders accept the risk — explicitly.

GET /v1/humanos/tuning/effective exposes the folded view for UI without pretending the log disappeared; rows past expires_at are omitted from the fold.

Trust expectation: “Undo” is a first-class story — not a hidden database edit.

Given / When / Then (use case 4)

Given a user mutes a source then issues revert_tuning, when effective is read, then mutes list is empty.

Given / When / Then (use case 5)

Given org scope without approval id, when POST tuning, then 403 explains the P8 gate.

Given / When / Then (use case 6)

Given product wants “temporary digest,” when they log set_digest_mode, then history remains queryable for support.