rvlib-shipkit 0.22.1 → 0.22.2
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/CHANGELOG.md +5 -0
- package/package.json +1 -1
- package/src/core/run-check.ts +3 -1
- package/src/core/run-ci.ts +7 -3
- package/src/serve/run-serve.ts +9 -3
- package/src/serve/serve-hub.ts +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.22.2
|
|
4
|
+
|
|
5
|
+
- **Frozen gates never write the motherhome row.** The bump preflight ate the machine dirt, then the gate INSIDE the bump re-wrote the local-projects row and the post-gate cleanliness check refused, the same wart's second head. A frozen run is release-shaped: it reports nothing to the motherhome, so the tree it proves clean stays clean. Interactive ci/check keep reporting.
|
|
6
|
+
- **The hub banner says only what exists.** It printed the hub URL twice (the banner plus the '/' tool row) and advertised a wiki graph in repos with no wiki: the default wiki graph now mounts only when a `wiki/` dir exists (a config-declared one always mounts), the '/' tool stays in /hub.json but never re-prints, and a game repo's startup reads one hub line plus the routes it really serves.
|
|
7
|
+
|
|
3
8
|
## 0.22.1
|
|
4
9
|
|
|
5
10
|
- **Fix: the cache stamp is baked in at RUN time, replays print it verbatim.** 0.22.0 annotated only the REPLAY, so a fresh run and a cache hit rendered different strings and STATUS.md still diffed on every alternation (his catch, the second one on this line). A TTL clean pass now writes "(cached at <stamp> until <stamp>)" into the line once, live output and every replay are byte-identical, and the line changes only when a re-validation happens. Proven: fresh-then-cached runs produce a zero-diff STATUS body.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rvlib-shipkit",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.2",
|
|
4
4
|
"description": "All-in-one repo companion: ci gate (task DAG), auto-discovered health checks, release-contract fulfillness, asks queue, md-wiki tools, asset atelier, live tui",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/core/run-check.ts
CHANGED
|
@@ -123,7 +123,9 @@ export async function collectChecks(root: string, o: CheckOpts = {}): Promise<Ch
|
|
|
123
123
|
const r = await suite.execute()
|
|
124
124
|
if (prevFresh == null) delete process.env.SHIPKIT_FRESH; else process.env.SHIPKIT_FRESH = prevFresh
|
|
125
125
|
if (prevFrozen == null) delete process.env.SHIPKIT_FROZEN; else process.env.SHIPKIT_FROZEN = prevFrozen
|
|
126
|
-
|
|
126
|
+
// frozen = a release-shaped run: reporting would re-dirty the tree the
|
|
127
|
+
// caller is about to demand clean (field wart 2026-08-05, second head)
|
|
128
|
+
if (o.frozen !== true) reportToMotherhome(root, c)
|
|
127
129
|
return { lines: r.lines, blocked: r.exitCode !== 0, advisoryFailures, undeclared }
|
|
128
130
|
}
|
|
129
131
|
|
package/src/core/run-ci.ts
CHANGED
|
@@ -120,9 +120,13 @@ export async function runCi(root: string, o: CiOpts = {}): Promise<number> {
|
|
|
120
120
|
} else sink(`WARN ${checks.advisoryFailures.length} non-blocking check(s) failing, about the world rather than this repo: ${checks.advisoryFailures.join(', ')}`)
|
|
121
121
|
}
|
|
122
122
|
const code = summary.ok && !fixFailed && !checks.blocked && !advisoryFatal && checks.undeclared.length === 0 ? 0 : 1
|
|
123
|
-
// motherhome row (docsBase wiki on this disk) — never affects the gate
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
// motherhome row (docsBase wiki on this disk) — never affects the gate,
|
|
124
|
+
// and never fires under frozen: a bump/release gate must not re-dirty
|
|
125
|
+
// the tree it just proved clean (field wart 2026-08-05, second head)
|
|
126
|
+
if (o.frozen !== true) {
|
|
127
|
+
const mh = reportToMotherhome(root, c)
|
|
128
|
+
if (mh.note != null) print(mh.note)
|
|
129
|
+
}
|
|
126
130
|
if (o.status !== false) {
|
|
127
131
|
// scope is named in the file: ci proves the gate, not repo health, and
|
|
128
132
|
// a bare GREEN here would claim clear-to-ship on a repo with red checks
|
package/src/serve/run-serve.ts
CHANGED
|
@@ -27,9 +27,14 @@ export function defaultHubPort(name: string): number {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export async function resolveHubDeps(root: string, c: RepoConfig, io: ServeIo, o?: { port?: number }): Promise<HubDeps> {
|
|
30
|
+
// the default wiki graph exists only when the repo HAS a wiki dir: a
|
|
31
|
+
// game repo's hub advertising a wiki route was startup noise (his catch
|
|
32
|
+
// 2026-08-05). A config-declared wikiGraph always mounts.
|
|
30
33
|
const wikiGraph = c.serve?.wikiGraph != null
|
|
31
34
|
? { title: c.serve.wikiGraph.title ?? `${c.name} wiki`, load: c.serve.wikiGraph.load }
|
|
32
|
-
:
|
|
35
|
+
: existsSync(join(root, 'wiki'))
|
|
36
|
+
? { title: `${c.name} wiki`, load: () => scanMdGraph({ root }) }
|
|
37
|
+
: null
|
|
33
38
|
const agentRoot = join(root, 'agent')
|
|
34
39
|
const agentGraph = existsSync(agentRoot)
|
|
35
40
|
? { title: `${c.name} agent/`, load: () => scanMdGraph({ root: agentRoot, index: c.serve?.agentIndex }) }
|
|
@@ -105,7 +110,8 @@ export async function runServe(root: string, io: ServeIo, o?: { port?: number })
|
|
|
105
110
|
const hub = serveHub(deps)
|
|
106
111
|
const base = `http://localhost:${hub.server.port}`
|
|
107
112
|
io.print(`ok: ${c.name} hub → ${base}`)
|
|
108
|
-
|
|
113
|
+
// the '/' tool IS the banner line above — printing it again was noise
|
|
114
|
+
for (const t of hub.tools) if (t.path !== '/') io.print(`ok: ${t.label} → ${base}${t.path}`)
|
|
109
115
|
// the off-machine door (phone, other machines) — publicHost is config
|
|
110
116
|
if (c.serve?.publicHost != null) io.print(`ok: public → http://${c.serve.publicHost}:${hub.server.port}/`)
|
|
111
117
|
await new Promise(() => {}) // the hub owns the process — never resolve
|
|
@@ -121,7 +127,7 @@ export async function runServe(root: string, io: ServeIo, o?: { port?: number })
|
|
|
121
127
|
// a re-run terminal stays as clickable as a first run — the
|
|
122
128
|
// owner's /hub.json tool list is the same source of truth
|
|
123
129
|
io.print(`ok: hub already served → http://localhost:${deps.port} (another window owns it)`)
|
|
124
|
-
for (const t of owner.tools ?? []) io.print(`ok: ${t.label} → http://localhost:${deps.port}${t.path}`)
|
|
130
|
+
for (const t of owner.tools ?? []) if (t.path !== '/') io.print(`ok: ${t.label} → http://localhost:${deps.port}${t.path}`)
|
|
125
131
|
return 0
|
|
126
132
|
}
|
|
127
133
|
io.printErr(`FAIL: port ${deps.port} is taken by ${owner?.name != null ? `repo '${owner.name}'` : 'another process'} — set serve: { port } in repo.config.ts to a free one`)
|
package/src/serve/serve-hub.ts
CHANGED
|
@@ -19,7 +19,8 @@ export type HubDeps = {
|
|
|
19
19
|
name: string
|
|
20
20
|
/** 0 = random (tests); real launches pass repo.config/default */
|
|
21
21
|
port: number
|
|
22
|
-
|
|
22
|
+
/** null = this repo has no wiki to graph — the route stays unmounted */
|
|
23
|
+
wikiGraph: HubGraph | null
|
|
23
24
|
/** null = no agent/ dir */
|
|
24
25
|
agentGraph: HubGraph | null
|
|
25
26
|
/** the media API (mounted under /media/*); null only if the route
|
|
@@ -44,7 +45,7 @@ export type Hub = { server: ReturnType<typeof Bun.serve>; tools: HubTool[]; stop
|
|
|
44
45
|
// so they can never disagree
|
|
45
46
|
export function hubTools(d: Pick<HubDeps, 'wikiGraph' | 'agentGraph'>): HubTool[] {
|
|
46
47
|
const tools: HubTool[] = [{ path: '/', label: 'hub', desc: 'THE page — media · asks · creds · posts · health · graphs' }]
|
|
47
|
-
tools.push({ path: '/graph/wiki', label: 'wiki graph', desc: d.wikiGraph.title })
|
|
48
|
+
if (d.wikiGraph != null) tools.push({ path: '/graph/wiki', label: 'wiki graph', desc: d.wikiGraph.title })
|
|
48
49
|
if (d.agentGraph != null) tools.push({ path: '/graph/agent', label: 'agent graph', desc: d.agentGraph.title })
|
|
49
50
|
return tools
|
|
50
51
|
}
|
|
@@ -124,8 +125,8 @@ export function serveHub(d: HubDeps): Hub {
|
|
|
124
125
|
return new Response(proc.stdout, { headers: { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' } })
|
|
125
126
|
}
|
|
126
127
|
const page = (s: string): Response => new Response(withHome(s), { headers: { 'content-type': 'text/html; charset=utf-8' } })
|
|
127
|
-
if (path === '/graph/wiki') return page(graphHtml(d.wikiGraph.load(), { title: d.wikiGraph.title }))
|
|
128
|
-
if (path === '/graph/wiki/graph.json') return Response.json(d.wikiGraph.load())
|
|
128
|
+
if (d.wikiGraph != null && path === '/graph/wiki') return page(graphHtml(d.wikiGraph.load(), { title: d.wikiGraph.title }))
|
|
129
|
+
if (d.wikiGraph != null && path === '/graph/wiki/graph.json') return Response.json(d.wikiGraph.load())
|
|
129
130
|
if (d.agentGraph != null && path === '/graph/agent') return page(graphHtml(d.agentGraph.load(), { title: d.agentGraph.title }))
|
|
130
131
|
if (d.agentGraph != null && path === '/graph/agent/graph.json') return Response.json(d.agentGraph.load())
|
|
131
132
|
// the media API namespace — the studio route table, ONE prefix strip
|