@adhdev/mesh-shared 1.0.82-rc.36 → 1.0.82-rc.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant-protocol.d.ts +11 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +147 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -8
- package/dist/index.mjs.map +1 -1
- package/dist/launch-defaults.d.ts +2 -0
- package/dist/mesh-tool-names.d.ts +2 -2
- package/dist/mission-git-state.d.ts +83 -0
- package/dist/mission-items.d.ts +2 -2
- package/dist/node-facts.d.ts +34 -0
- package/dist/release.d.ts +90 -0
- package/dist/turn-ipc-mission.d.ts +5 -0
- package/package.json +1 -1
- package/src/assistant-protocol.ts +19 -1
- package/src/index.ts +2 -0
- package/src/launch-defaults.ts +2 -0
- package/src/mesh-tool-names.ts +8 -0
- package/src/mission-git-state.ts +112 -0
- package/src/mission-items.ts +2 -2
- package/src/node-facts.ts +34 -0
- package/src/release.ts +121 -0
- package/src/turn-ipc-mission.ts +9 -4
package/src/release.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* release — types, limits and pure helpers for release objects
|
|
3
|
+
* (docs/design/2026-11-01-release-objects.md).
|
|
4
|
+
*
|
|
5
|
+
* A release is a mesh-scoped entity that groups MISSIONS (explicit membership) and records
|
|
6
|
+
* deploy facts. It is distinct from the vendor release stamp (a build channel stamp) and
|
|
7
|
+
* from `.adhdev/preview-deploy.json` (the per-checkout deploy record it snapshots).
|
|
8
|
+
* Membership is only ever created by an explicit call; git evaluation and deploy heads
|
|
9
|
+
* produce suggestions, never members. Dependency-free leaf.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const MESH_RELEASE_CHANNELS = ['preview', 'stable'] as const
|
|
13
|
+
export type MeshReleaseChannel = typeof MESH_RELEASE_CHANNELS[number]
|
|
14
|
+
export function isMeshReleaseChannel(value: unknown): value is MeshReleaseChannel {
|
|
15
|
+
return typeof value === 'string' && (MESH_RELEASE_CHANNELS as readonly string[]).includes(value)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const MESH_RELEASE_STATUSES = ['planned', 'frozen', 'deployed', 'verified', 'superseded', 'cancelled'] as const
|
|
19
|
+
export type ReleaseStatus = typeof MESH_RELEASE_STATUSES[number]
|
|
20
|
+
export function isReleaseStatus(value: unknown): value is ReleaseStatus {
|
|
21
|
+
return typeof value === 'string' && (MESH_RELEASE_STATUSES as readonly string[]).includes(value)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const RELEASE_MEMBER_STATES = ['member', 'moved_out', 'removed'] as const
|
|
25
|
+
export type ReleaseMemberState = typeof RELEASE_MEMBER_STATES[number]
|
|
26
|
+
|
|
27
|
+
/** Who may write release membership. The daemon never adds a member on its own. */
|
|
28
|
+
export const RELEASE_ACTORS = ['coordinator', 'owner', 'assistant', 'script'] as const
|
|
29
|
+
export type ReleaseActor = typeof RELEASE_ACTORS[number]
|
|
30
|
+
|
|
31
|
+
export const RELEASE_EVENT_TYPES = [
|
|
32
|
+
'created', 'status_changed', 'scope_added', 'scope_moved', 'scope_removed',
|
|
33
|
+
'deploy_recorded', 'changelog_drafted', 'changelog_applied',
|
|
34
|
+
] as const
|
|
35
|
+
export type ReleaseEventType = typeof RELEASE_EVENT_TYPES[number]
|
|
36
|
+
|
|
37
|
+
/** Mission-side events recorded when membership changes (so the mission history shows it). */
|
|
38
|
+
export const MISSION_RELEASE_EVENT_TYPES = ['release_attached', 'release_moved', 'release_detached'] as const
|
|
39
|
+
|
|
40
|
+
export const RELEASE_LIMITS = {
|
|
41
|
+
name: 80,
|
|
42
|
+
note: 500,
|
|
43
|
+
/** `mesh_missions.release_note`: one user/operator-visible sentence. */
|
|
44
|
+
releaseNote: 400,
|
|
45
|
+
missionsPerCall: 100,
|
|
46
|
+
changelogEntries: 300,
|
|
47
|
+
} as const
|
|
48
|
+
|
|
49
|
+
/** Deploy targets snapshot (a copy of `.adhdev/preview-deploy.json` `targets`). */
|
|
50
|
+
export interface ReleaseDeployTarget {
|
|
51
|
+
commit?: string
|
|
52
|
+
deployedAt?: string
|
|
53
|
+
previewVersion?: string
|
|
54
|
+
[key: string]: unknown
|
|
55
|
+
}
|
|
56
|
+
export type ReleaseTargets = Partial<Record<'npm' | 'server' | 'web', ReleaseDeployTarget>> & Record<string, ReleaseDeployTarget | undefined>
|
|
57
|
+
|
|
58
|
+
export const CHANGELOG_SECTIONS = ['Added', 'Fixed', 'Changed'] as const
|
|
59
|
+
export type ChangelogSection = typeof CHANGELOG_SECTIONS[number]
|
|
60
|
+
|
|
61
|
+
export interface ChangelogDraftEntry {
|
|
62
|
+
missionId: string
|
|
63
|
+
/** `<!-- m:<id8> -->` — the idempotency marker `--apply` writes after the line. */
|
|
64
|
+
marker: string
|
|
65
|
+
section: ChangelogSection
|
|
66
|
+
/** The sentence that would be written to CHANGELOG.md. */
|
|
67
|
+
text: string
|
|
68
|
+
source: 'release_note' | 'summary'
|
|
69
|
+
/** True when `text` is not user-facing wording yet (carries the needs-wording marker). */
|
|
70
|
+
needsWording?: boolean
|
|
71
|
+
/** Evidence kept in the draft JSON only; never written to CHANGELOG.md. */
|
|
72
|
+
evidence?: { gitState?: string; outcome?: string; shas?: string[] }
|
|
73
|
+
/** Non-blocking lint warnings (internal ids / wave numbers / forbidden phrases). */
|
|
74
|
+
lint?: string[]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ChangelogDraftExcluded {
|
|
78
|
+
missionId: string
|
|
79
|
+
reason: string
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ChangelogDraft {
|
|
83
|
+
generatedAt: string
|
|
84
|
+
entries: ChangelogDraftEntry[]
|
|
85
|
+
/** Member missions without a `release_note` (target 0 at deploy time). */
|
|
86
|
+
needsNote: string[]
|
|
87
|
+
excluded: ChangelogDraftExcluded[]
|
|
88
|
+
/** Member missions whose refs are not confirmed on the default branch (`not_landed` / `unknown`). */
|
|
89
|
+
warnings: Array<{ missionId: string; gitState: string; message: string }>
|
|
90
|
+
appliedAt?: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const NEEDS_WORDING_MARKER = '[needs user-facing wording]'
|
|
94
|
+
|
|
95
|
+
/** Marker that makes `--apply` idempotent: one per mission entry inside CHANGELOG.md. */
|
|
96
|
+
export function changelogMissionMarker(missionId: string): string {
|
|
97
|
+
return `<!-- m:${missionId.slice(0, 8)} -->`
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Default section for an entry; `[Fixed]`/`[Added]`/`[Changed]` prefixes on the note pin it. */
|
|
101
|
+
export function classifyChangelogSection(text: string, hint?: string): { section: ChangelogSection; text: string } {
|
|
102
|
+
const m = /^\s*\[(added|fixed|changed)\]\s*/i.exec(text)
|
|
103
|
+
if (m) {
|
|
104
|
+
const key = m[1].toLowerCase()
|
|
105
|
+
return { section: key === 'added' ? 'Added' : key === 'fixed' ? 'Fixed' : 'Changed', text: text.slice(m[0].length).trim() }
|
|
106
|
+
}
|
|
107
|
+
const probe = `${hint ?? ''} ${text}`.toLowerCase()
|
|
108
|
+
if (/\b(fix|fixes|fixed|bug|regression|crash|error)\b|오류|결함|버그|수정/.test(probe)) return { section: 'Fixed', text }
|
|
109
|
+
if (/\b(add|adds|added|new|introduc\w*|support\w*)\b|신규|추가/.test(probe)) return { section: 'Added', text }
|
|
110
|
+
return { section: 'Changed', text }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Phrases that must not reach a public CHANGELOG (internal waves, rc numbering, ids). Warning-only. */
|
|
114
|
+
export function lintChangelogText(text: string): string[] {
|
|
115
|
+
const out: string[] = []
|
|
116
|
+
if (/\bwave[\s-]*\d+/i.test(text) || /웨이브\s*\d+/.test(text)) out.push('mentions an internal wave number')
|
|
117
|
+
if (/\brc[.\s-]?\d+\b/i.test(text)) out.push('mentions an internal rc number')
|
|
118
|
+
if (/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i.test(text) || /\bmi_[0-9a-f]{12}\b/.test(text) || /\brel_[0-9a-f]{12}\b/.test(text)) out.push('contains an internal id')
|
|
119
|
+
if (/\bmission\b/i.test(text)) out.push('mentions "mission" (internal vocabulary)')
|
|
120
|
+
return out
|
|
121
|
+
}
|
package/src/turn-ipc-mission.ts
CHANGED
|
@@ -94,6 +94,8 @@ export interface MissionUpsertRequest {
|
|
|
94
94
|
*/
|
|
95
95
|
structured?: boolean
|
|
96
96
|
summary?: string
|
|
97
|
+
/** One user/operator-visible sentence for the release CHANGELOG draft (design 2026-11-01-release-objects L5). `''` clears. */
|
|
98
|
+
releaseNote?: string
|
|
97
99
|
outcome?: string
|
|
98
100
|
outcomeReason?: string
|
|
99
101
|
/** Mission id open items move to when this call closes the mission (default: the carry-over mission). */
|
|
@@ -114,6 +116,7 @@ export interface MeshMissionOpenItemCountsWire {
|
|
|
114
116
|
/** Structured-missions fields (only with `structured: true` on the request). */
|
|
115
117
|
export interface MeshMissionStructuredWire {
|
|
116
118
|
summary?: string
|
|
119
|
+
releaseNote?: string
|
|
117
120
|
outcome?: string
|
|
118
121
|
outcomeReason?: string
|
|
119
122
|
version?: number
|
|
@@ -137,7 +140,7 @@ export interface MissionUpsertResponse {
|
|
|
137
140
|
result?: Record<string, unknown>
|
|
138
141
|
}
|
|
139
142
|
|
|
140
|
-
const STRUCTURED_MISSION_KEYS = ['summary', 'outcome', 'outcomeReason', 'version', 'carryOver', 'openItems'] as const
|
|
143
|
+
const STRUCTURED_MISSION_KEYS = ['summary', 'releaseNote', 'outcome', 'outcomeReason', 'version', 'carryOver', 'openItems'] as const
|
|
141
144
|
|
|
142
145
|
function isOpenItemCountsWire(value: unknown): value is MeshMissionOpenItemCountsWire {
|
|
143
146
|
if (!isRecord(value) || !hasOnlyKeys(value, ['remaining', 'live_check', 'decision', 'evidence'])) return false
|
|
@@ -145,7 +148,7 @@ function isOpenItemCountsWire(value: unknown): value is MeshMissionOpenItemCount
|
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
function isStructuredMissionFields(value: Record<string, unknown>): boolean {
|
|
148
|
-
for (const k of ['summary', 'outcome', 'outcomeReason'] as const) {
|
|
151
|
+
for (const k of ['summary', 'releaseNote', 'outcome', 'outcomeReason'] as const) {
|
|
149
152
|
if (value[k] !== undefined && typeof value[k] !== 'string') return false
|
|
150
153
|
}
|
|
151
154
|
if (value.version !== undefined && !isNonNegativeInt(value.version)) return false
|
|
@@ -165,7 +168,7 @@ function isMeshMissionRecordWire(value: unknown): value is MeshMissionRecordWire
|
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
function isMissionUpsertRequest(value: unknown): value is MissionUpsertRequest {
|
|
168
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'id', 'title', 'goal', 'status', 'brief', 'structured', 'summary', 'outcome', 'outcomeReason', 'carryOverTo', 'toCarryOver', 'itemsAdd', 'itemsUpdate'])) return false
|
|
171
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'id', 'title', 'goal', 'status', 'brief', 'structured', 'summary', 'releaseNote', 'outcome', 'outcomeReason', 'carryOverTo', 'toCarryOver', 'itemsAdd', 'itemsUpdate'])) return false
|
|
169
172
|
if (value.v !== TURN_IPC_PROTOCOL_VERSION) return false
|
|
170
173
|
if (!isEvidenceIdentifier(value.meshId)) return false
|
|
171
174
|
if (value.id !== undefined && !isEvidenceIdentifier(value.id)) return false
|
|
@@ -174,7 +177,7 @@ function isMissionUpsertRequest(value: unknown): value is MissionUpsertRequest {
|
|
|
174
177
|
if (value.title.trim().length === 0 && value.toCarryOver !== true && value.id === undefined) return false
|
|
175
178
|
if (value.structured !== undefined && typeof value.structured !== 'boolean') return false
|
|
176
179
|
if (value.toCarryOver !== undefined && typeof value.toCarryOver !== 'boolean') return false
|
|
177
|
-
for (const k of ['summary', 'outcome', 'outcomeReason'] as const) {
|
|
180
|
+
for (const k of ['summary', 'releaseNote', 'outcome', 'outcomeReason'] as const) {
|
|
178
181
|
if (value[k] !== undefined && typeof value[k] !== 'string') return false
|
|
179
182
|
}
|
|
180
183
|
if (value.carryOverTo !== undefined && !isEvidenceIdentifier(value.carryOverTo)) return false
|
|
@@ -324,6 +327,7 @@ export interface MissionListSummaryVerboseWire {
|
|
|
324
327
|
closeCandidateEmittedAt?: string | null
|
|
325
328
|
/** Structured lists only. */
|
|
326
329
|
summary?: string
|
|
330
|
+
releaseNote?: string
|
|
327
331
|
outcome?: string
|
|
328
332
|
outcomeReason?: string
|
|
329
333
|
version?: number
|
|
@@ -351,6 +355,7 @@ export interface MissionListSummarySlimWire {
|
|
|
351
355
|
closeCandidateEmittedAt?: string | null
|
|
352
356
|
/** Structured lists only. */
|
|
353
357
|
summary?: string
|
|
358
|
+
releaseNote?: string
|
|
354
359
|
outcome?: string
|
|
355
360
|
outcomeReason?: string
|
|
356
361
|
version?: number
|