From 8aadfc5a7df25de78dc53f7fbe3890f1c8142bd4 Mon Sep 17 00:00:00 2001 From: Vikas Singhal Date: Fri, 31 Jul 2026 16:09:12 +0530 Subject: [PATCH] fix(dreaming): drop opaque ids from topics; require a sample before the approval nag (v0.281.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two defects visible in the first guidance lines the rebuilt topic maps produced on live tenants. - `f90fc16d7fb9a19` appeared as something "the fleet frequently works on". The digit rule exists for `v3`/`php8` but also admits hex handles; a long hex/base36 run (or a `tsk_…`-style prefixed id) is an identifier, not a name. - The approval-friction signal fired on instawp off THREE decisions (0 approved, 3 rejected = 100%). v0.280.0 fixed the missing denominator; a rate still needs a sample. Both the guidance line and the `policy.review` recommendation now also require ≥8 human approval decisions in the window. 137/137 governance + 18/18 tier-A policy. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 11 +++++++++++ package-lock.json | 4 ++-- package.json | 2 +- src/edge/dreaming.ts | 10 +++++++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ec60ca..480aee2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,17 @@ new version heading in the same commit. ## [Unreleased] +## [0.281.3] — 2026-07-31 +### Fixed +- **Opaque identifiers are no longer "things the fleet works on".** The digit rule that admits `v3`/`php8` + also admitted hex handles — after the topic-map rebuild, live instawp surfaced `f90fc16d7fb9a19` in the + guidance line. A long hex/base36 run (or a `tsk_…`-style prefixed id) is a handle, not a name. +- **The approval-friction signal now needs a sample, not just a rate.** v0.280.0 replaced a raw-count gate + with a ≥20% rejection rate; on live instawp that fired off **3 decisions** (0 approved, 3 rejected = + 100%) — directionally true for the window but far too thin to tell an owner their policy is + miscalibrated, or to tell every agent to expect rejection. Both the guidance line and the + `policy.review` recommendation now also require at least 8 human approval decisions in the window. + ## [0.281.2] — 2026-07-31 ### Fixed - **A fixed topic extractor now actually reaches workspaces that have already been running.** `topics` is a diff --git a/package-lock.json b/package-lock.json index 81c973a..0c54488 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "agent-os", - "version": "0.281.2", + "version": "0.281.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "agent-os", - "version": "0.281.2", + "version": "0.281.3", "license": "MIT", "bin": { "agent-os": "bin/agent-os" diff --git a/package.json b/package.json index 3fb2120..27905fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-os", - "version": "0.281.2", + "version": "0.281.3", "description": "A generic, governed operating system for running autonomous agents safely across brands. Ships with a local web console.", "license": "MIT", "type": "commonjs", diff --git a/src/edge/dreaming.ts b/src/edge/dreaming.ts index 36bc254..a243e2e 100644 --- a/src/edge/dreaming.ts +++ b/src/edge/dreaming.ts @@ -417,6 +417,10 @@ function properNouns(episodes: EpisodeRow[]): Set { * and the guidance line only prints at all when ≥2 topics survive. */ function isEntity(token: string, proper: Set): boolean { + // An opaque IDENTIFIER is never a topic. The digit rule below exists for `v3`/`php8`, but it also let + // hex ids through — live instawp surfaced `f90fc16d7fb9a19` as something "the fleet frequently works + // on". A long hex/base36 run with no vowel structure is a handle, not a name. + if (/^[0-9a-f]{8,}$/i.test(token) || /^[a-z]{2,4}_[0-9a-f]{6,}$/i.test(token)) return false; // A FILENAME qualifies on its base name, never its extension — otherwise the dot rule (meant for // hostnames and versions) admits every path an agent mentions, including format placeholders like // `yyyy-mm-dd.md`. `.com`/`.io` are not code extensions, so real hostnames still pass below. @@ -474,9 +478,13 @@ function recentTally(s: DreamState): { sessions: number; success: number; approv * it self-corrects on the next pass, which records the denominator. */ const REJECTION_RATE = 0.2; +/** A rate also needs a SAMPLE. Live instawp fired the recommendation off 3 decisions (0 approved, + * 3 rejected = 100%) — directionally true for that window, but far too thin to tell an owner their + * policy is miscalibrated. Below this many human decisions the signal stays quiet. */ +const MIN_APPROVAL_DECISIONS = 8; function rejectionRate(t: { approved: number; rejected: number }): number { const n = t.approved + t.rejected; - return n ? t.rejected / n : 0; + return n >= MIN_APPROVAL_DECISIONS ? t.rejected / n : 0; } // Cadence is OFF (everyHours 0) → learned guidance goes stale after this long unrefreshed. When a cadence