@theronap/agnoclast-mcp 0.9.208 โ 0.9.210
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/lib/identifier_routes.mjs +81 -0
- package/lib/server.mjs +59 -21
- package/package.json +1 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// WHERE AN IDENTIFIER ROUTES, AND WHAT A FILING SESSION MAY CLAIM โ pure renderers (ADR-0075, 2026-10-05).
|
|
2
|
+
//
|
|
3
|
+
// ๐ด THE LINE THIS REPLACES TOLD A SESSION SOMETHING FALSE. `read_page "file:โฆ"` printed "Home: X" for
|
|
4
|
+
// the page whose BODY carried the [[stamp]], and for an unclaimed identifier said "author its page and
|
|
5
|
+
// stamp [[id]] on it (that page becomes the home)". Since ADR-0026 a body stamp routes nothing โ the
|
|
6
|
+
// router reads page_routing_identifiers only โ so following that advice produced a page the router never
|
|
7
|
+
// sends anything to, and the session was told it worked. That is the 2026-10-04 V4 email-surface
|
|
8
|
+
// incident. The server now answers `homes` from CLAIMS and moves stamp pages to `mentionedOn`; this
|
|
9
|
+
// prints the two apart and gives `set_routing_identifier` as the fix.
|
|
10
|
+
//
|
|
11
|
+
// Pure so CI can execute what each state prints.
|
|
12
|
+
|
|
13
|
+
const q = (s) => `"${s}"`
|
|
14
|
+
const pageLabel = (h) => `${q(h.title)} (${h.kind}, ${h.tier})`
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The read_page identifier view, as lines. Handles the pre-ADR-0075 server too (no `mentionedOn`):
|
|
18
|
+
* there `homes` are stamp pages, so they are printed as mentions, never as where anything routes.
|
|
19
|
+
*/
|
|
20
|
+
export function renderIdentifierResolution(out, { expand = false } = {}) {
|
|
21
|
+
const id = out.identifier
|
|
22
|
+
const lines = [`# ${id} (identifier โ a join key, not a page)`]
|
|
23
|
+
const claimAware = Array.isArray(out.mentionedOn)
|
|
24
|
+
const homes = claimAware ? (out.homes ?? []) : []
|
|
25
|
+
const mentions = claimAware ? out.mentionedOn : (out.homes ?? [])
|
|
26
|
+
const catchAll = claimAware ? (out.catchAll ?? []) : []
|
|
27
|
+
const hidden = claimAware ? Number(out.hiddenClaims ?? 0) : 0
|
|
28
|
+
|
|
29
|
+
if (!claimAware) {
|
|
30
|
+
lines.push('This server cannot say where it ROUTES (it predates ADR-0075); the pages below only mention it, and a mention routes nothing. `my_routing_claims` lists the claims that do.')
|
|
31
|
+
} else if (homes.length === 1 && out.status !== 'duplicate') {
|
|
32
|
+
const h = homes[0]
|
|
33
|
+
const how = h.via === id ? 'claims it' : `claims the folder \`${h.via}\` above it`
|
|
34
|
+
lines.push(`Routes to: ${pageLabel(h)} โ it ${how}. \`read_page ${q(h.title)}\` for the page.`)
|
|
35
|
+
} else if (homes.length >= 1) {
|
|
36
|
+
lines.push(out.status === 'duplicate'
|
|
37
|
+
? `โ More than one page claims this at the same depth โ a record carrying it is filed under all of them (and STAGES if they sit in different brains). Settle which one it belongs to:`
|
|
38
|
+
: `Routes to ${homes.length} pages, deepest claim first โ a record carrying it is filed under each:`)
|
|
39
|
+
for (const h of homes) lines.push(` - ${pageLabel(h)} via \`${h.via}\`${h.brain ? ` ยท ${h.brain}` : ''}`)
|
|
40
|
+
}
|
|
41
|
+
if (hidden > 0) {
|
|
42
|
+
lines.push(`${hidden} claim${hidden === 1 ? '' : 's'} on page${hidden === 1 ? '' : 's'} you cannot open โ ${hidden === 1 ? 'it routes' : 'they route'} all the same.`)
|
|
43
|
+
}
|
|
44
|
+
if (claimAware && homes.length === 0 && hidden === 0) {
|
|
45
|
+
const fallback = catchAll.length
|
|
46
|
+
? ` Records carrying it are filed only under the repo catch-all: ${catchAll.map((h) => q(h.title)).join(', ')} (via \`${catchAll[0].via}\`).`
|
|
47
|
+
: ''
|
|
48
|
+
lines.push(
|
|
49
|
+
`No page claims this, so it routes nowhere on its own.${fallback} If a page is ABOUT it, claim it there: ` +
|
|
50
|
+
`\`set_routing_identifier\` with identifier ${q(id)}${id.startsWith('file:') && !id.endsWith('/') ? ' (or the deepest folder above it, with its trailing slash)' : ''}. ` +
|
|
51
|
+
'A [[stamp]] in a page body does NOT make that page its home.',
|
|
52
|
+
)
|
|
53
|
+
} else if (catchAll.length) {
|
|
54
|
+
lines.push(`Also filed under the repo catch-all: ${catchAll.map((h) => q(h.title)).join(', ')} (via \`${catchAll[0].via}\`).`)
|
|
55
|
+
}
|
|
56
|
+
if (mentions.length) {
|
|
57
|
+
lines.push(`Mentioned on (does not route): ${mentions.map((h) => q(h.title)).join(', ')}`)
|
|
58
|
+
}
|
|
59
|
+
const ev = out.events ?? { visibleCount: 0, recent: [] }
|
|
60
|
+
lines.push(`ยท ${ev.visibleCount} tagged timeline event${ev.visibleCount === 1 ? '' : 's'} visible to you${ev.recent?.length ? ':' : expand ? '.' : ' โ pass expand: true to list recent ones.'}`)
|
|
61
|
+
for (const e of ev.recent ?? []) lines.push(` - ${String(e.occurredAt).slice(0, 10)} ยท ${e.source} ยท ${e.summary}`)
|
|
62
|
+
return lines
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** intake_materialize: the folder-claim offers, as lines. [] when the server sent none (or is older). */
|
|
66
|
+
export function renderFolderClaimOffers(offers) {
|
|
67
|
+
if (!Array.isArray(offers) || offers.length === 0) return []
|
|
68
|
+
const lines = ['Folder claim offered (nothing was claimed โ decide, do not just accept):']
|
|
69
|
+
for (const o of offers) if (o && typeof o.offer === 'string') lines.push(` - ${o.offer}`)
|
|
70
|
+
return lines.length > 1 ? lines : []
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** author / edit_page: [[file:โฆ]] stamps the write left that route nothing to this page, as lines. */
|
|
74
|
+
export function renderUnroutedPathStamps(body) {
|
|
75
|
+
const stamps = Array.isArray(body?.unroutedPathStamps) ? body.unroutedPathStamps : []
|
|
76
|
+
if (!stamps.length) return []
|
|
77
|
+
const lines = [`โ ${stamps.length} [[file:โฆ]] stamp${stamps.length === 1 ? '' : 's'} on this page route${stamps.length === 1 ? 's' : ''} nothing here: ${stamps.map((s) => `\`${s.identifier}\``).join(', ')}.`]
|
|
78
|
+
if (typeof body.unroutedPathStampsNote === 'string') lines.push(body.unroutedPathStampsNote)
|
|
79
|
+
for (const s of stamps) if (typeof s.fix === 'string') lines.push(` - ${s.fix}`)
|
|
80
|
+
return lines
|
|
81
|
+
}
|
package/lib/server.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { seatChargeRefusal, seatChargeQuestion } from './seat_charge.mjs'
|
|
|
9
9
|
import { fetchCortex, classify, resolveBase, resolveEnvToken, setSessionKey, setClientVersion, runWithTool, heartbeatPolicy } from './diagnose.mjs'
|
|
10
10
|
import { REPORT_KINDS, REPORT_ISSUE_DESCRIPTION, fileIssue } from './report_issue.mjs'
|
|
11
11
|
import { folderClaimLine } from './folder_claims.mjs'
|
|
12
|
+
import { renderFolderClaimOffers, renderIdentifierResolution, renderUnroutedPathStamps } from './identifier_routes.mjs'
|
|
12
13
|
import { resolveSessionKey, resolveLogSessionId } from './session_key.mjs'
|
|
13
14
|
import { runSendImessage } from './imessage_send.mjs'
|
|
14
15
|
import { formatGrepHits } from './grep_cli.mjs'
|
|
@@ -1343,6 +1344,9 @@ function renderNudge(payload) {
|
|
|
1343
1344
|
// null/undefined no container was ever requested (say nothing)
|
|
1344
1345
|
// containerSkipped is the only one that needs a warning: it means a container WAS asked for and
|
|
1345
1346
|
// did not happen, which is exactly the silence this whole path exists to remove.
|
|
1347
|
+
// ADR-0075 โ the deepest unclaimed folder this unit sits in, offered for the feature page it was
|
|
1348
|
+
// filed onto. Never claimed by the server; the session decides.
|
|
1349
|
+
lines.push(...renderFolderClaimOffers(body?.folderClaimOffers))
|
|
1346
1350
|
if (body?.containerSkipped) {
|
|
1347
1351
|
lines.push(`โ CONTAINER NOT APPLIED โ ${body.containerSkipped}. The record exists but is not inside the event you named; place it with contain_record or leave it out.`)
|
|
1348
1352
|
} else if (body?.contained === true) {
|
|
@@ -1874,7 +1878,7 @@ function renderNudge(payload) {
|
|
|
1874
1878
|
description:
|
|
1875
1879
|
'READ the full authored wiki page for one node (project/person/org/you) by its canonical name โ every section you can see. This is how you READ a node; `grep` only LOCATES pages (snippets + their [[links]]), it does not read them. Navigate like a researcher: read the page you need, then FOLLOW its inline [[links]] by calling read_page on each linked name โ keep following while the linked pages stay relevant, stop when they do not. You decide how deep to go. Returns only what you are permitted to see.',
|
|
1876
1880
|
inputSchema: {
|
|
1877
|
-
name: z.string().describe('the canonical node name exactly as written (e.g. "Agnoclast", "Ben", or a [[link]] target) โ identifier links ([[repo:owner/name]]) resolve to
|
|
1881
|
+
name: z.string().describe('the canonical node name exactly as written (e.g. "Agnoclast", "Ben", or a [[link]] target) โ identifier links ([[repo:owner/name]], [[file:owner/name:path]]) resolve to the page(s) that CLAIM them โ where the router actually files records โ plus the pages that merely mention them, and a visible-event count'),
|
|
1878
1882
|
kind: z.enum(['project', 'person', 'org', 'user']).optional().describe('node kind โ a PREFERENCE, not a filter. The lookup matches every kind by title and only prefers this one when several share a name, so omitting it never hides a person/org/user page. Pass it to disambiguate a name that exists as more than one kind.'),
|
|
1879
1883
|
expand: z.boolean().optional().describe('identifier names only: also list recent visible timeline events for this identifier (default: home + count)'),
|
|
1880
1884
|
history: z.boolean().optional().describe('node names only: return the node\'s TIMELINE (events joined via its [[repo:โฆ]] stamps, reverse-chron, viewer-visible) instead of the page body. The page is the present; this is the history.'),
|
|
@@ -1962,18 +1966,8 @@ function renderNudge(payload) {
|
|
|
1962
1966
|
const r = await fetchCortex(`${BASE}/api/identifier/resolve?${qs}`, { headers: { Authorization: `Bearer ${TOKEN}` } })
|
|
1963
1967
|
if (r.ok) {
|
|
1964
1968
|
const out = await r.json()
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
lines.push(`No authored home carries this stamp yet โ data but no home. If you know what this identifies, author its page and stamp [[${out.identifier}]] on it (that page becomes the home).`)
|
|
1968
|
-
} else if (out.status === 'healthy') {
|
|
1969
|
-
const h = out.homes[0]
|
|
1970
|
-
lines.push(`Home: "${h.title}" (${h.kind}, ${h.tier}) โ \`read_page "${h.title}"\` for the full page.`)
|
|
1971
|
-
} else {
|
|
1972
|
-
lines.push(`โ ${out.homes.length} pages carry this stamp โ possible duplicate homes, consider reconciling:`)
|
|
1973
|
-
for (const h of out.homes) lines.push(` - "${h.title}" (${h.kind}, ${h.tier})`)
|
|
1974
|
-
}
|
|
1975
|
-
lines.push(`ยท ${out.events.visibleCount} tagged timeline event${out.events.visibleCount === 1 ? '' : 's'} visible to you${out.events.recent.length ? ':' : expand ? '.' : ' โ pass expand: true to list recent ones.'}`)
|
|
1976
|
-
for (const e of out.events.recent) lines.push(` - ${String(e.occurredAt).slice(0, 10)} ยท ${e.source} ยท ${e.summary}`)
|
|
1969
|
+
// ADR-0075: `homes` are CLAIMS (where the router files it); stamp pages are `mentionedOn`.
|
|
1970
|
+
const lines = renderIdentifierResolution(out, { expand })
|
|
1977
1971
|
// KWA-28 โ an identifier node is VIRTUAL: derived per read, zero stored rows, so there is no
|
|
1978
1972
|
// as_of to fetch. The item says exactly what to do in that case: "where the object is derived
|
|
1979
1973
|
// per-read, stamp the read itself." Note this as-of means something DIFFERENT from every
|
|
@@ -3988,7 +3982,10 @@ function renderNudge(payload) {
|
|
|
3988
3982
|
const undatedNote = out?.undated === true
|
|
3989
3983
|
? `\nโ This section now carries no explicit calendar date. A reader can see WHEN the text was written but not when the claim was TRUE. If it asserts a status, add the date inline โ you still hold the context. The edit already landed; this is advisory.`
|
|
3990
3984
|
: ''
|
|
3991
|
-
|
|
3985
|
+
// ADR-0075 โ a [[file:โฆ]] stamp this edit wrote that routes nothing here, with the claim that would.
|
|
3986
|
+
const unroutedLines = renderUnroutedPathStamps(out)
|
|
3987
|
+
const unrouted = unroutedLines.length ? `\n${unroutedLines.join('\n')}` : ''
|
|
3988
|
+
return { content: [{ type: 'text', text: `Edited "${name}" (${out.brain} ยท ${out.tier} tier) ยง ${out.heading}. Only that passage changed; every other section is byte-identical. New version: ${out.version}${red}${undatedNote}${unrouted}` }] }
|
|
3992
3989
|
},
|
|
3993
3990
|
)
|
|
3994
3991
|
|
|
@@ -5515,9 +5512,10 @@ function renderNudge(payload) {
|
|
|
5515
5512
|
ref: z.string().optional().describe('the page\'s stable id, printed as `ref:` by read_page. PREFER THIS over name when you have it: a ref is unique across brains, so it addresses exactly one page and never needs a brain to disambiguate it.'),
|
|
5516
5513
|
to: z.string().describe('who becomes the owner โ an org member\'s email, or their user id. Must be an ACTIVE member of the same brain as the page. Pass your own address to take ownership yourself.'),
|
|
5517
5514
|
confirm_audience: z.string().optional().describe('ONLY after a refusal that lists who would start reading the page. A reports / peers / down page is addressed to its OWNER\'s team, so a new owner means a new audience; the first call is refused with the names and a confirm token. Pass that token here once the person asking has seen the list and means it. The token covers exactly that list โ if the page or the org chart changed since, it is refused again with the new one. Never guess it and never send it on a first call.'),
|
|
5515
|
+
tier_decision: z.enum(['keep', 'delegate', 'accessible', 'down', 'reports', 'peers', 'scoped', 'confidential']).optional().describe('REQUIRED for a reports / peers / down page (Theron, 2026-10-03: whoever hands one over re-evaluates its tier then and there). "keep" = same tier, re-aimed at the new owner\'s team; a tier = owner and tier change together; "delegate" = the new owner decides, and until they do NOTHING about the page changes โ same owner, same readers โ while they may read it to decide. Ask the person which they want; never pick one for them. Omit it for any other tier (it transfers as before).'),
|
|
5518
5516
|
},
|
|
5519
5517
|
},
|
|
5520
|
-
async ({ kind, name, ref, to, confirm_audience }) => {
|
|
5518
|
+
async ({ kind, name, ref, to, confirm_audience, tier_decision }) => {
|
|
5521
5519
|
let res
|
|
5522
5520
|
try {
|
|
5523
5521
|
res = await fetchCortex(`${BASE}/api/brain/node-policy`, {
|
|
@@ -5525,7 +5523,7 @@ function renderNudge(payload) {
|
|
|
5525
5523
|
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
5526
5524
|
// No `tier`: a node has exactly one page (ADR-0064 ยง4), and the route resolves it. Sending a
|
|
5527
5525
|
// guessed tier is how this used to 404 on precisely the scoped/confidential pages it matters for.
|
|
5528
|
-
body: JSON.stringify({ kind, ...(name ? { name } : {}), ...(ref ? { ref } : {}), transfer_to: to, ...(confirm_audience ? { confirm_audience } : {}) }),
|
|
5526
|
+
body: JSON.stringify({ kind, ...(name ? { name } : {}), ...(ref ? { ref } : {}), transfer_to: to, ...(confirm_audience ? { confirm_audience } : {}), ...(tier_decision ? { tier_decision } : {}) }),
|
|
5529
5527
|
})
|
|
5530
5528
|
} catch (e) {
|
|
5531
5529
|
return toolError(`Could not transfer ownership: ${e.message}`)
|
|
@@ -5538,10 +5536,21 @@ function renderNudge(payload) {
|
|
|
5538
5536
|
const d = classify(res.status, res.headers.get('content-type'), '', res.headers.get('x-vercel-id'))
|
|
5539
5537
|
return toolError(`Could not transfer ownership: ${d.message}`)
|
|
5540
5538
|
}
|
|
5539
|
+
if (out?.pending_for) {
|
|
5540
|
+
return {
|
|
5541
|
+
content: [{
|
|
5542
|
+
type: 'text',
|
|
5543
|
+
text: `Handed over for re-evaluation โ ${to} decides this page's tier. Nothing about the page has changed: it is still ${out.tier}, owned and read exactly as before, until they choose.${out.reader_granted ? ' They were given read access so they can decide.' : ''} They see it at their next session start and complete it with confirm_page_ownership.`,
|
|
5544
|
+
}],
|
|
5545
|
+
}
|
|
5546
|
+
}
|
|
5547
|
+
const retier = out?.retiered
|
|
5548
|
+
? ` It moved from ${out.retiered.from} to ${out.retiered.to} in the same step${out.retiered.cascaded_records ? `; ${out.retiered.cascaded_records} record(s) filed under it followed the narrowing` : ''}.`
|
|
5549
|
+
: ` The page is still ${out.tier}${tier_decision === 'keep' ? ', now addressed to its new owner\'s team' : '; transferring at this tier does not re-tier, so check that its audience is still right'}.`
|
|
5541
5550
|
return {
|
|
5542
5551
|
content: [{
|
|
5543
5552
|
type: 'text',
|
|
5544
|
-
text: `Done โ ${name ?? out.ref} now belongs to ${to}
|
|
5553
|
+
text: `Done โ ${name ?? out.ref} now belongs to ${to}.${retier}${renderAudienceChange(out.audience_change)}`,
|
|
5545
5554
|
}],
|
|
5546
5555
|
}
|
|
5547
5556
|
},
|
|
@@ -5555,13 +5564,39 @@ function renderNudge(payload) {
|
|
|
5555
5564
|
'confirm_page_ownership',
|
|
5556
5565
|
{
|
|
5557
5566
|
title: 'Confirm pages handed to you',
|
|
5558
|
-
description: 'When someone hands you a page โ a colleague using transfer_page_ownership, or a steward rehoming a departed member\'s page โ your session start lists it under "Pages handed to you" until you confirm it. Pass the page `refs` (as listed there) to confirm those, or `all: true` for every open one. Confirming only clears the notice: the page is already yours. To pass one on instead, use transfer_page_ownership.',
|
|
5567
|
+
description: 'When someone hands you a page โ a colleague using transfer_page_ownership, or a steward rehoming a departed member\'s page โ your session start lists it under "Pages handed to you" until you confirm it. Pass the page `refs` (as listed there) to confirm those, or `all: true` for every open one. Confirming only clears the notice: the page is already yours. To pass one on instead, use transfer_page_ownership. A page listed under "Pages waiting on your tier decision" is DIFFERENT: it is not yours yet, and stays exactly as it was until you choose. Take it with `ref` + `tier_decision` ("keep" its tier, re-aimed at your team, or a tier); `all` never takes those.',
|
|
5559
5568
|
inputSchema: {
|
|
5560
5569
|
refs: z.array(z.string()).optional().describe('page refs, as listed under "Pages handed to you"'),
|
|
5561
|
-
all: z.boolean().optional().describe('true = confirm every open notice'),
|
|
5570
|
+
all: z.boolean().optional().describe('true = confirm every open notice (never takes a page waiting on your tier decision)'),
|
|
5571
|
+
ref: z.string().optional().describe('ONE page waiting on your tier decision, as listed under "Pages waiting on your tier decision"'),
|
|
5572
|
+
tier_decision: z.enum(['keep', 'accessible', 'down', 'reports', 'peers', 'scoped', 'confidential']).optional().describe('with `ref`: "keep" its tier (re-aimed at your team) or the tier it should have. Ask the person; never choose for them.'),
|
|
5573
|
+
confirm_audience: z.string().optional().describe('ONLY after a refusal listing who would start reading the page โ the token from it, once the person has seen the list and means it.'),
|
|
5562
5574
|
},
|
|
5563
5575
|
},
|
|
5564
|
-
async ({ refs, all }) => {
|
|
5576
|
+
async ({ refs, all, ref, tier_decision, confirm_audience }) => {
|
|
5577
|
+
if (ref || tier_decision) {
|
|
5578
|
+
if (!ref || !tier_decision) return toolError('To take a page waiting on your tier decision, pass both `ref` and `tier_decision`.')
|
|
5579
|
+
let res
|
|
5580
|
+
try {
|
|
5581
|
+
res = await fetchCortex(`${BASE}/api/brain/ownership-notices`, {
|
|
5582
|
+
method: 'POST',
|
|
5583
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
5584
|
+
body: JSON.stringify({ ref, tier_decision, ...(confirm_audience ? { confirm_audience } : {}) }),
|
|
5585
|
+
})
|
|
5586
|
+
} catch (e) {
|
|
5587
|
+
return toolError(`Could not take the page: ${e.message}`)
|
|
5588
|
+
}
|
|
5589
|
+
const out = await res.json().catch(() => null)
|
|
5590
|
+
if (!res.ok) {
|
|
5591
|
+
if (out?.error) return toolError(`Could not take the page: ${out.error}`)
|
|
5592
|
+
const d = classify(res.status, res.headers.get('content-type'), '', res.headers.get('x-vercel-id'))
|
|
5593
|
+
return toolError(`Could not take the page: ${d.message}`)
|
|
5594
|
+
}
|
|
5595
|
+
const moved = out?.from_tier && out.from_tier !== out.tier
|
|
5596
|
+
? ` It moved from ${out.from_tier} to ${out.tier}${out.cascaded_records ? `; ${out.cascaded_records} record(s) filed under it followed the narrowing` : ''}.`
|
|
5597
|
+
: ` It stays ${out?.tier}, now addressed to your team.`
|
|
5598
|
+
return { content: [{ type: 'text', text: `Done โ the page is yours.${moved}${renderAudienceChange(out?.audience_change)}` }] }
|
|
5599
|
+
}
|
|
5565
5600
|
if (!all && !(refs && refs.length)) return toolError('Pass `refs` (the pages to confirm) or `all: true`.')
|
|
5566
5601
|
let res
|
|
5567
5602
|
try {
|
|
@@ -6029,8 +6064,11 @@ function renderNudge(payload) {
|
|
|
6029
6064
|
` can read the page. Re-send with identity_claim_ack: true only if the subject is publishing` +
|
|
6030
6065
|
` their own contact detail; otherwise use a confidential section or set_routing_identifier.`
|
|
6031
6066
|
: ''
|
|
6067
|
+
// ADR-0075 โ [[file:โฆ]] stamps that route nothing here, each with the claim that would. Advisory.
|
|
6068
|
+
const unroutedLines = renderUnroutedPathStamps(out)
|
|
6069
|
+
const unrouted = unroutedLines.length ? `\n${unroutedLines.join('\n')}` : ''
|
|
6032
6070
|
const verb = out?.created ? 'Created + authored' : 'Authored'
|
|
6033
|
-
const note = out?.built ? `${verb} "${name}" (${out.built} tier${out.built === 1 ? '' : 's'}). Links: ${blue} resolved, ${retired} retired, ${red} red.${corrections}${retiredList}${redList}${stamps}${undated}${identityRefused}`
|
|
6071
|
+
const note = out?.built ? `${verb} "${name}" (${out.built} tier${out.built === 1 ? '' : 's'}). Links: ${blue} resolved, ${retired} retired, ${red} red.${corrections}${retiredList}${redList}${stamps}${undated}${identityRefused}${unrouted}`
|
|
6034
6072
|
: `No change to "${name}"${out?.skipped?.length ? ` (${out.skipped.join(', ')})` : ''}.${corrections}`
|
|
6035
6073
|
return { content: [{ type: 'text', text: note }] }
|
|
6036
6074
|
},
|
package/package.json
CHANGED