docs: replace vague provenance prose with recorded facts

This commit is contained in:
Turtle
2026-08-09 15:35:02 +08:00
parent 8c124f84b6
commit 9704749b01
380 changed files with 946 additions and 874 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/session/session-title/README.md
README.md: 13a2a1c298c3abb8c8987b9373edc46a071ce9db
README.zh.md: 075e9e31fc7acbc76f0e3fd7a2642b4d879569d3
README.md: e7004eb2e03757cb28b4b5756a876e1dee4b6229
README.zh.md: 45c1aafdf214f63ae7a071d2558eaefec13ea70f

View File

@@ -29,7 +29,7 @@ All limits are required; the library supplies no defaults.
## Provider contract
A provider supplies a branded stable id, automatic mode (`first-message` or `all-user-messages`), and `generate(request)`. The request carries the live session, all eligible messages through one fixed revision, the current logged main-request route when available, and cancellation. The result identifies a non-empty title, unique ordered source-message seqs from that request, and optional model provenance. The service normalizes and validates the result before it becomes durable.
A provider supplies a branded stable id, automatic mode (`first-message` or `all-user-messages`), and `generate(request)`. The request carries the live session, all eligible messages through one fixed revision, the current logged main-request route when available, and cancellation. The result identifies a non-empty title, unique ordered source-message seqs from that request, and the optional provider/model route used to generate it. The service normalizes and validates the result before it becomes durable.
See the [session-title data structures](../../../docs/subsystems/session-title.md) and [implemented decision](../../../.agents/notes/implemented/feature/2026-07-21-log-backed-session-titles.md).

View File

@@ -29,7 +29,7 @@ Fork 出的会话会原样继承种子中的标题事件。首消息节奏不会
## 提供方约定
提供方会提供带品牌类型的稳定 id、自动模式`first-message``all-user-messages`)和 `generate(request)`。请求携带活跃会话、截至一次固定修订的所有符合条件消息、可用时当前已记录的主请求路由,以及取消信号。结果包含非空标题、该请求中唯一且有序的来源消息 seq以及可选的模型来源信息。服务会在结果持久保存前进行规范化和验证。
提供方会提供带品牌类型的稳定 id、自动模式`first-message``all-user-messages`)和 `generate(request)`。请求携带活跃会话、截至一次固定修订的所有符合条件消息、可用时当前已记录的主请求路由,以及取消信号。结果包含非空标题、该请求中唯一且有序的来源消息 seq以及生成该标题时使用的可选提供方/模型路由。服务会在结果持久保存前进行规范化和验证。
参见[会话标题数据结构](../../../docs/subsystems/session-title.md)与[已实现决策](../../../.agents/notes/implemented/feature/2026-07-21-log-backed-session-titles.md)。

View File

@@ -63,7 +63,7 @@ export interface SessionTitleEventData {
readonly title: string
/** Exact human `user/message` seqs used to derive this title; empty for an explicit user rename. */
readonly messageSeqs: number[]
/** Built-in fallback, registered-provider, or explicit-user provenance. */
/** Whether the built-in fallback, a registered provider, or the user supplied the title. */
readonly source: SessionTitleSource
}
@@ -146,14 +146,14 @@ export interface SessionTitleProviderResult {
/** One optional asynchronous title implementation registered with the service. */
export interface SessionTitleProvider {
/** Stable provider identity recorded in title provenance. */
/** Stable id of the provider recorded with the title. */
readonly id: SessionTitleProviderId
/** When new human prompts start automatic generation. */
readonly automatic: SessionTitleAutomaticMode
/**
* Produce one title revision.
* @param request - message snapshot, current route, session, and cancellation.
* @returns proposed title plus exact input seqs and optional model provenance.
* @returns proposed title plus exact input seqs and the optional provider/model route used to generate it.
*/
generate(request: SessionTitleProviderRequest): Promise<SessionTitleProviderResult>
}
@@ -615,12 +615,12 @@ export class SessionTitleService extends Service {
let model: SessionTitleModelProvenance | undefined
if (modelCandidate !== undefined) {
if (modelCandidate === null || typeof modelCandidate !== 'object') {
throw new Error('session-title provider model provenance requires non-empty provider and model')
throw new Error('session-title provider result model must contain non-empty provider and model strings')
}
const record = modelCandidate as Record<string, unknown>
if (typeof record.provider !== 'string' || record.provider.length === 0
|| typeof record.model !== 'string' || record.model.length === 0) {
throw new Error('session-title provider model provenance requires non-empty provider and model')
throw new Error('session-title provider result model must contain non-empty provider and model strings')
}
model = { provider: record.provider, model: record.model }
}

View File

@@ -16,7 +16,7 @@ export const name = 'session-title-invariant'
export const inject = ['invariants']
/**
* Durable title-provenance invariant: an automatic title always cites at
* Durable title-source invariant: an automatic title always cites at
* least one human `user/message` seq, and an explicit user rename cites none
* — `messageSeqs` is empty iff `source.kind` is `user`. Provider revisions
* are validated by the service before their append; this checks the durable
@@ -32,7 +32,8 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant
if (event.type !== 'session/title') return
const { source, messageSeqs } = event.data
if ((messageSeqs.length === 0) !== (source.kind === 'user')) {
fail(`session/title event ${String(event.seq)} breaks provenance: source "${source.kind}" with ${String(messageSeqs.length)} cited message seq(s)`)
const requirement = source.kind === 'user' ? 'cite no message seqs' : 'cite at least one message seq'
fail(`session/title event ${String(event.seq)} with source "${source.kind}" must ${requirement}; got ${String(messageSeqs.length)}`)
}
}, { global: true })
}, { inject: ['sessions'] })

View File

@@ -1,4 +1,4 @@
// Title-provenance invariant: messageSeqs is empty iff source.kind is 'user'
// Title-source invariant: `messageSeqs` is empty iff `source.kind` is `user`.
// — the durable relationship every appended session/title event must keep.
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
@@ -14,7 +14,7 @@ async function setup(): Promise<Context> {
return ctx
}
describe('session-title provenance invariant', () => {
describe('session-title source invariant', () => {
it('accepts cited automatic titles and citation-free user renames', async () => {
const ctx = await setup()
const session = ctx.sessions.create(SessionId('title-invariant-valid'))

View File

@@ -436,12 +436,12 @@ describe('SessionTitleService provider validation and stale scheduling', () => {
{ value: { title: 'valid', messageSeqs: [999] }, error: /unique, ordered seqs/ },
{ value: { title: 'valid', messageSeqs: [first.seq, first.seq] }, error: /unique, ordered seqs/ },
{ value: { title: 'valid', messageSeqs: [second.seq, first.seq] }, error: /unique, ordered seqs/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: null }, error: /model provenance/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: 'route' }, error: /model provenance/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 1, model: 'm' } }, error: /model provenance/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: '', model: 'm' } }, error: /model provenance/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 'p', model: 1 } }, error: /model provenance/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 'p', model: '' } }, error: /model provenance/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: null }, error: /provider result model/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: 'route' }, error: /provider result model/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 1, model: 'm' } }, error: /provider result model/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: '', model: 'm' } }, error: /provider result model/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 'p', model: 1 } }, error: /provider result model/ },
{ value: { title: 'valid', messageSeqs: [first.seq], model: { provider: 'p', model: '' } }, error: /provider result model/ },
]
for (const item of cases) {
result = item.value