import type { ArtifactKind, DeriveManagedStateInput, DerivedManagedState, ManagedArtifactRecord, ManagedArtifactSource, ManagedDecision, ManagedDistributionTarget, ManagedEvidenceRef, ManagedObservation } from '../types/index.js'; /** * 受管记录的 per-record 文件存储。一条记录一个 `.omk/managed/.json`,镜像 report-store 的 * 成熟模式(原子 tmp+rename):每次 install 只碰自己那个文件,independent write、不丢别人、 * 天然可扩展。无数据库——这个规模(每项目几十条、CLI 单写者)JSON 文件足够,且保住"可读可 * grep 可 diff"的透明价值。 * * **消费方无关的核心层**:本模块不依赖 CLI、不打印、不退出,所有函数接收显式 `dir`。CLI 命令只是 * 众多消费方之一;omk 长成平台后 server / web / SDK 复用同一模块,甚至换 DB 实现也只需镜像这组 * load/upsert 签名。记录的构造统一走 `buildManagedArtifactRecord`,新增字段只改这一处、所有消费方继承。 */ export declare function managedDir(cwd?: string): string; export declare function globalManagedDir(): string; export declare function recordPath(dir: string, id: string): string; /** 稳定身份 = hash(kind, name)。源路径是可变属性、不进 id。kind 取自固定枚举(无 `|`),分隔可注入。 */ export declare function managedRecordId(kind: ArtifactKind, name: string): string; export { hashArtifactSource, isDistributablePath, distributableCopyFilter } from '../inputs/content-hash.js'; /** git commit SHA 形态:7–64 位 hex。写入(evidence.ts)与读取校验共用同一判定,避免写读不对称 * (写时不校验、读时却要求 SHA → 自己写进去的值重载时被自己判脏)。 */ export declare function isShaLike(v: unknown): v is string; export declare function loadManagedRecord(dir: string, id: string): ManagedArtifactRecord | null; /** 读全部记录。项目目录空 → 兜底全局(镜像 observe inbox 的 project→global)。 */ export declare function loadAllManagedRecords(dir?: string): ManagedArtifactRecord[]; /** * 哪个 managed 目录是权威(有记录的那个):项目目录非空取项目,否则全局非空取全局,都空回项目。 * 与 `loadAllManagedRecords` 的 project→global 回退**同口径** —— 写方(append evidence)据此写回 * 读方实际取记录的同一目录,避免"读全局、写项目"把证据落到空目录。 */ export declare function resolveManagedDir(dir?: string): string; /** * 纯合并逻辑(无 IO):install 写的是事实,绝不动 evidence/decisions(那是 eval/promote 的地盘)。 * - distribution 按 path 去重(同路径以新值替换); * - evidence / decisions 一律保留旧值(install 带来的恒为空)——这是设计要的"版本历史 + 附带证据", * 用来支撑 rollback;**但保留 ≠ 当作当前有效**:每条 evidence 携带它测的 contentHash,读时由 * `deriveManagedState` 只把与当前 contentHash 匹配的 evidence 算作当前证据,所以重装到新内容后 * 旧证据仍在记录里(可回滚),却不会让新内容被读成 measurable; * - installedAt 保留首次("under management since");contentHash / source 刷新到本次安装。 */ export declare function mergeManagedRecord(prev: ManagedArtifactRecord | null, next: ManagedArtifactRecord): ManagedArtifactRecord; /** 读旧记录 → 合并 → 原子 tmp+rename 只写该 id 的文件。返回合并后记录。 */ export declare function upsertManagedRecord(dir: string, record: ManagedArtifactRecord): ManagedArtifactRecord; /** * 追加一条评测证据(append-only)。与 install 的 upsert 路径相反——`mergeManagedRecord` 刻意**保留旧 * evidence、丢弃 next.evidence**(install 只写事实),所以证据写入不能走 upsert,必须独立 load→push→ * 原子重写。按 `(reportId, contentHash)` 去重:重跑同一份 eval 不堆重复条目(reportId 含运行身份, * 同内容重测会是新 reportId → 新条目,正是要的版本史)。记录不存在(未 install / 名字不匹配)返回 * null —— 这是"管理是 install 显式 opt-in"的体现:eval 绝不为未纳管的 skill 凭空建记录。 */ export declare function appendManagedEvidence(dir: string, recordId: string, evidence: ManagedEvidenceRef): ManagedArtifactRecord | null; /** * 追加一条 observe 生产健康观测(append-only,#235)。同 evidence 不走 upsert:`mergeManagedRecord` 保留旧 * observations、丢弃 next 的(install 只写事实)。按 `reportId` 去重——observe 无 contentHash,一份 observe 报告 * 对一条观测;同报告重跑不堆重复,不同报告(新窗口)是新 reportId → 新条目。记录不存在(未 install / 名字不 * 匹配)返回 null —— 与 evidence 同口径:observe 绝不为未纳管 skill 凭空建记录(管理是 install 显式 opt-in)。 */ export declare function appendManagedObservation(dir: string, recordId: string, observation: ManagedObservation): ManagedArtifactRecord | null; /** * 把受管记录的 drift 基线(contentHash)重锚到新源内容哈,保留 evidence / decisions / source / * distribution 全不动。用于 `omk evolve` 把胜出版本写回 source 后,让记录跟上实际内容——否则记录的 * contentHash 仍指旧基线,list 会把被 evolve 改过的受管 skill 永久显示成 stale。 * * 只动 contentHash:旧 hash 锚定的旧 evidence / promote 决定自动变「非当前」(其 contentHash ≠ 新基线)= * 历史,不再冒充当前状态;新内容的证据 / 决定由调用方另行 append。已经是该 hash → no-op 不重写。记录不存在 * 返回 null(同 evidence / decision:管理是 install 显式 opt-in)。 */ export declare function rebaselineManagedContentHash(dir: string, recordId: string, newHash: string): ManagedArtifactRecord | null; /** * 当前内容(contentHash === record.contentHash)是否处于 promoted 态。决定是 append-only 事件流, * promote 与 rollback 互为反操作,故不能看「是否存在过 promote」,而要看**当前内容最近一条** promote/rollback * 决定是不是 promote —— rollback 之后再 promote 仍能恢复 promoted,反之亦然。换了内容(contentHash 变)后 * 旧内容的决定一概不算数。 * * 「最近」按 decidedAt 真实时刻取,与 `latestCurrentEvidence` 同口径:omk 自写恒 UTC `Z`、字典序即时间序; * 但记录可手改 / 随仓库分发,异偏移 ISO 串字典序会乱 → 两端可解析才用解析时刻,否则退字典序;并列取后出现的 * (数组追加序 = 事件序)。 */ export declare function isCurrentlyPromoted(record: ManagedArtifactRecord): boolean; /** * 当前 promoted 版本是否经 override(--force)采用 —— 返回该 override(verdict + 被绕过的门),否则 undefined。 * 仅当前内容最近一条决定是 promote 且带 override 才有值;rollback 之后(已撤销接受)返回 undefined。供 list / * Studio **读时审计**:从总览一眼看出哪些当前采用是越门来的。override 的**写**仍只在 CLI(`promote --force`), * Studio 不执行——见 evidence-gated-management.md §9(#238)。 */ export declare function currentPromoteOverride(record: ManagedArtifactRecord): ManagedDecision['override']; /** * 追加一条人工管理决定(append-only,promote/reject/rollback 走此路)。与 evidence 同样**不能走 upsert** * (`mergeManagedRecord` 刻意保留旧 decisions、丢弃 next.decisions),必须独立 load→push→原子重写。 * * 幂等:对**当前内容**的 promote/rollback,看是否会改变当前 promoted 态——已 promoted 再 promote、未 promoted * 再 rollback 都不追加、原样返回(由 CLI 提示「已 promoted」/「未 promoted」)。这让重复 `omk promote` / * `omk rollback` 不堆冗余事件,但 promote↔rollback 的真实切换、以及换了内容(contentHash 变)后的新决定仍会 * 追加——正是要的版本史。其它 decisionKind(如 reject)仍按「同 kind + 同 contentHash 即 dedup」。记录不存在 * (未 install)返回 null,与 evidence 同口径(管理是 install 显式 opt-in,绝不为未纳管 skill 凭空建记录)。 */ export declare function appendManagedDecision(dir: string, recordId: string, decision: ManagedDecision): ManagedArtifactRecord | null; /** * 统一的记录构造点——所有消费方(CLI / 未来 server / SDK)经此组装,保证 schema 一致。 * 只接收事实(身份、源、hash、分发落点);evidence/decisions 恒为空(eval/promote 的地盘)。 */ export declare function buildManagedArtifactRecord(input: { name: string; kind: ArtifactKind; source: ManagedArtifactSource; contentHash: string; installedAt: string; distribution: ManagedDistributionTarget[]; id?: string; }): ManagedArtifactRecord; /** install 调用的便捷封装。 */ export declare function recordManagedArtifact(record: ManagedArtifactRecord, opts?: { dir?: string; }): ManagedArtifactRecord; /** * 读时推导生命周期标签。 * - 源缺失或 hash 漂(当前内容 ≠ 记录 contentHash)→ stale; * - 否则当前内容已有 promote 决定 → promoted; * - 否则有**当前**证据 / samples → measurable; * - 否则 installed。 * 「当前证据 / 当前决定」= contentHash 与记录当前 contentHash 匹配的 evidence / decision —— 旧内容的不算数, * 这正是 #203「证据必须跟 artifact 一起走」的读时保证。`'discovered'` 留给无分发的记录,此函数不产生。 */ export declare function deriveManagedState(input: DeriveManagedStateInput): DerivedManagedState; /** 一条观测是否构成「确诊生产盲区」:healthBand 红 **且** 统计够力。underpowered(数据不足)是 §6.1 的 * unknown —— 既不放行也不拦截,不当确诊盲区;yellow / green 也不算(留信息展示,避免低样本噪声里过度报警)。 */ export declare function isProductionGapObservation(obs: ManagedObservation): boolean; export interface ProductionGapState { /** `gap` = 确诊生产盲区(red + 够力);`underpowered` = 线上数据不足、不可知;`none` = 无观测 / 最新观测非红。 */ marker: 'none' | 'gap' | 'underpowered'; latest?: ManagedObservation; } /** * observe 生产健康观测的**读时 marker**(#235)。取**最新一条**观测(latest-wins,按 observedAt = 被观测 * 流量窗口结束时刻)分类:red + 够力 → `gap`;underpowered → `underpowered`(数据不足);其余 → `none`。 * * **版本无关的生产信号**:observe 量的是线上**部署版**的行为,而记录里没有可靠的「源码版 ↔ 部署版」时间锚 * (`evolve` 改写源、`rebaselineManagedContentHash` 只换 `contentHash`,不碰 distribution/installedAt;部署副本 * 仍是旧的)——所以这里**不按源码版归因、也不臆造版本闸门**(那只会给假精度、还会在源码 bump 后压掉仍然有效的 * 真盲区)。marker 反映的是部署版的近况,可能滞后于当前源码版,UI / spec 据此老实标注。 * * **绝不翻 stale / 不动生命周期枚举**(§6.1:只有内容漂移翻 stale,observe 是信号源不是受控 eval)。 */ export declare function deriveProductionGap(record: ManagedArtifactRecord): ProductionGapState;