@anthropologies/claudestory 0.1.62 → 0.1.63

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/index.d.ts CHANGED
@@ -550,12 +550,13 @@ declare class ProjectState {
550
550
  readonly totalTicketCount: number;
551
551
  readonly openTicketCount: number;
552
552
  readonly completeTicketCount: number;
553
- readonly openIssueCount: number;
553
+ readonly activeIssueCount: number;
554
554
  readonly issuesBySeverity: ReadonlyMap<IssueSeverity, number>;
555
555
  readonly activeNoteCount: number;
556
556
  readonly archivedNoteCount: number;
557
557
  readonly activeLessonCount: number;
558
558
  readonly deprecatedLessonCount: number;
559
+ readonly lessonTags: readonly string[];
559
560
  constructor(input: {
560
561
  tickets: Ticket[];
561
562
  issues: Issue[];
@@ -748,6 +749,12 @@ declare function readHandover(handoversDir: string, filename: string): Promise<s
748
749
  * Returns the date string or null if the filename does not conform.
749
750
  */
750
751
  declare function extractHandoverDate(filename: string): string | null;
752
+ /**
753
+ * Extracts the human-readable title from a handover filename.
754
+ * Strips `.md` extension and YYYY-MM-DD date prefix, converts remaining
755
+ * hyphens to spaces, and trims whitespace. Mirrors Swift parseHandoverFilename.
756
+ */
757
+ declare function extractHandoverTitle(filename: string): string;
751
758
 
752
759
  interface UmbrellaProgress {
753
760
  readonly total: number;
@@ -1385,6 +1392,7 @@ declare const SnapshotV1Schema: z.ZodObject<{
1385
1392
  type: string;
1386
1393
  file: string;
1387
1394
  }>, "many">>;
1395
+ gitHead: z.ZodOptional<z.ZodString>;
1388
1396
  }, "strip", z.ZodTypeAny, {
1389
1397
  issues: z.objectOutputType<{
1390
1398
  id: z.ZodString;
@@ -1499,6 +1507,7 @@ declare const SnapshotV1Schema: z.ZodObject<{
1499
1507
  type: string;
1500
1508
  file: string;
1501
1509
  }[] | undefined;
1510
+ gitHead?: string | undefined;
1502
1511
  }, {
1503
1512
  issues: z.objectInputType<{
1504
1513
  id: z.ZodString;
@@ -1613,6 +1622,7 @@ declare const SnapshotV1Schema: z.ZodObject<{
1613
1622
  file: string;
1614
1623
  }[] | undefined;
1615
1624
  handoverFilenames?: string[] | undefined;
1625
+ gitHead?: string | undefined;
1616
1626
  }>;
1617
1627
  type SnapshotV1 = z.infer<typeof SnapshotV1Schema>;
1618
1628
  /**
@@ -1739,6 +1749,12 @@ interface SnapshotDiff {
1739
1749
  removed: string[];
1740
1750
  };
1741
1751
  }
1752
+ interface RecapStaleness {
1753
+ status: "behind" | "diverged";
1754
+ snapshotSha: string;
1755
+ currentSha: string;
1756
+ commitsBehind?: number;
1757
+ }
1742
1758
  interface RecapResult {
1743
1759
  snapshot: {
1744
1760
  filename: string;
@@ -1759,6 +1775,7 @@ interface RecapResult {
1759
1775
  recentlyClearedBlockers: string[];
1760
1776
  };
1761
1777
  partial: boolean;
1778
+ staleness?: RecapStaleness;
1762
1779
  }
1763
1780
  /**
1764
1781
  * Computes the diff between a snapshot state and the current state.
@@ -1770,7 +1787,7 @@ declare function diffStates(snapshotState: ProjectState, currentState: ProjectSt
1770
1787
  declare function buildRecap(currentState: ProjectState, snapshotInfo: {
1771
1788
  snapshot: SnapshotV1;
1772
1789
  filename: string;
1773
- } | null): RecapResult;
1790
+ } | null, root: string): Promise<RecapResult>;
1774
1791
 
1775
1792
  interface ActiveSessionSummary {
1776
1793
  readonly sessionId: string;
@@ -1854,4 +1871,4 @@ declare function formatRecap(recap: RecapResult, state: ProjectState, format: Ou
1854
1871
  declare function formatExport(state: ProjectState, mode: "all" | "phase", phaseId: string | null, format: OutputFormat): string;
1855
1872
  declare function formatRecommendations(result: RecommendResult, state: ProjectState, format: OutputFormat): string;
1856
1873
 
1857
- export { type Blocker, BlockerSchema, CURRENT_SCHEMA_VERSION, type Config, ConfigSchema, DATE_REGEX, DateSchema, EMPTY_SCAFFOLD_HEADING, ERROR_CODES, type ErrorCode, type ErrorEnvelope, ExitCode, type ExitCodeValue, type Features, FeaturesSchema, INTEGRITY_WARNING_TYPES, ISSUE_ID_REGEX, ISSUE_SEVERITIES, ISSUE_STATUSES, type InitOptions, type InitResult, type Issue, IssueIdSchema, IssueSchema, type IssueSeverity, type IssueStatus, type LoadOptions, type LoadResult, type LoadWarning, type LoadWarningType, NOTE_ID_REGEX, NOTE_STATUSES, type NextTicketAllBlocked, type NextTicketAllComplete, type NextTicketEmpty, type NextTicketOutcome, type NextTicketResult, type Note, NoteIdSchema, NoteSchema, type NoteStatus, OUTPUT_FORMATS, type OutputFormat, type PartialEnvelope, type Phase, PhaseSchema, type PhaseStatus, type PhaseWithStatus, ProjectLoaderError, ProjectState, type RecapResult, type RecommendCategory, type RecommendItemKind, type RecommendResult, type Recommendation, type Roadmap, RoadmapSchema, type SnapshotDiff, type SnapshotV1, SnapshotV1Schema, type SuccessEnvelope, TICKET_ID_REGEX, TICKET_STATUSES, TICKET_TYPES, type Ticket, TicketIdSchema, TicketSchema, type TicketStatus, type TicketType, type UmbrellaProgress, type UnblockImpact, type ValidationFinding, type ValidationLevel, type ValidationResult, type WithProjectLockOptions, atomicWrite, blockedTickets, buildRecap, currentPhase, deleteIssue, deleteNote, deleteTicket, descendantLeaves, diffStates, discoverProjectRoot, errorEnvelope, escapeMarkdownInline, extractHandoverDate, fencedBlock, formatBlockedTickets, formatBlockerList, formatError, formatExport, formatHandoverContent, formatHandoverCreateResult, formatHandoverList, formatInitResult, formatIssue, formatIssueList, formatNextTicketOutcome, formatPhaseList, formatPhaseTickets, formatRecap, formatRecommendations, formatSnapshotResult, formatStatus, formatTicket, formatTicketList, formatValidation, guardPath, initProject, isBlockerCleared, listHandovers, loadLatestSnapshot, loadProject, mergeValidation, nextIssueID, nextNoteID, nextOrder, nextTicket, nextTicketID, nextTickets, partialEnvelope, phasesWithStatus, readHandover, recommend, runTransaction, runTransactionUnlocked, saveSnapshot, serializeJSON, sortKeysDeep, successEnvelope, ticketsUnblockedBy, umbrellaProgress, validateProject, withProjectLock, writeConfig, writeIssue, writeIssueUnlocked, writeNote, writeNoteUnlocked, writeRoadmap, writeRoadmapUnlocked, writeTicket, writeTicketUnlocked };
1874
+ export { type Blocker, BlockerSchema, CURRENT_SCHEMA_VERSION, type Config, ConfigSchema, DATE_REGEX, DateSchema, EMPTY_SCAFFOLD_HEADING, ERROR_CODES, type ErrorCode, type ErrorEnvelope, ExitCode, type ExitCodeValue, type Features, FeaturesSchema, INTEGRITY_WARNING_TYPES, ISSUE_ID_REGEX, ISSUE_SEVERITIES, ISSUE_STATUSES, type InitOptions, type InitResult, type Issue, IssueIdSchema, IssueSchema, type IssueSeverity, type IssueStatus, type LoadOptions, type LoadResult, type LoadWarning, type LoadWarningType, NOTE_ID_REGEX, NOTE_STATUSES, type NextTicketAllBlocked, type NextTicketAllComplete, type NextTicketEmpty, type NextTicketOutcome, type NextTicketResult, type Note, NoteIdSchema, NoteSchema, type NoteStatus, OUTPUT_FORMATS, type OutputFormat, type PartialEnvelope, type Phase, PhaseSchema, type PhaseStatus, type PhaseWithStatus, ProjectLoaderError, ProjectState, type RecapResult, type RecapStaleness, type RecommendCategory, type RecommendItemKind, type RecommendResult, type Recommendation, type Roadmap, RoadmapSchema, type SnapshotDiff, type SnapshotV1, SnapshotV1Schema, type SuccessEnvelope, TICKET_ID_REGEX, TICKET_STATUSES, TICKET_TYPES, type Ticket, TicketIdSchema, TicketSchema, type TicketStatus, type TicketType, type UmbrellaProgress, type UnblockImpact, type ValidationFinding, type ValidationLevel, type ValidationResult, type WithProjectLockOptions, atomicWrite, blockedTickets, buildRecap, currentPhase, deleteIssue, deleteNote, deleteTicket, descendantLeaves, diffStates, discoverProjectRoot, errorEnvelope, escapeMarkdownInline, extractHandoverDate, extractHandoverTitle, fencedBlock, formatBlockedTickets, formatBlockerList, formatError, formatExport, formatHandoverContent, formatHandoverCreateResult, formatHandoverList, formatInitResult, formatIssue, formatIssueList, formatNextTicketOutcome, formatPhaseList, formatPhaseTickets, formatRecap, formatRecommendations, formatSnapshotResult, formatStatus, formatTicket, formatTicketList, formatValidation, guardPath, initProject, isBlockerCleared, listHandovers, loadLatestSnapshot, loadProject, mergeValidation, nextIssueID, nextNoteID, nextOrder, nextTicket, nextTicketID, nextTickets, partialEnvelope, phasesWithStatus, readHandover, recommend, runTransaction, runTransactionUnlocked, saveSnapshot, serializeJSON, sortKeysDeep, successEnvelope, ticketsUnblockedBy, umbrellaProgress, validateProject, withProjectLock, writeConfig, writeIssue, writeIssueUnlocked, writeNote, writeNoteUnlocked, writeRoadmap, writeRoadmapUnlocked, writeTicket, writeTicketUnlocked };
package/dist/index.js CHANGED
@@ -173,12 +173,13 @@ var ProjectState = class _ProjectState {
173
173
  totalTicketCount;
174
174
  openTicketCount;
175
175
  completeTicketCount;
176
- openIssueCount;
176
+ activeIssueCount;
177
177
  issuesBySeverity;
178
178
  activeNoteCount;
179
179
  archivedNoteCount;
180
180
  activeLessonCount;
181
181
  deprecatedLessonCount;
182
+ lessonTags;
182
183
  constructor(input) {
183
184
  this.tickets = input.tickets;
184
185
  this.issues = input.issues;
@@ -259,19 +260,19 @@ var ProjectState = class _ProjectState {
259
260
  lByID.set(l.id, l);
260
261
  }
261
262
  this.lessonsByID = lByID;
262
- this.totalTicketCount = input.tickets.length;
263
- this.openTicketCount = input.tickets.filter(
263
+ this.totalTicketCount = this.leafTickets.length;
264
+ this.openTicketCount = this.leafTickets.filter(
264
265
  (t) => t.status !== "complete"
265
266
  ).length;
266
- this.completeTicketCount = input.tickets.filter(
267
+ this.completeTicketCount = this.leafTickets.filter(
267
268
  (t) => t.status === "complete"
268
269
  ).length;
269
- this.openIssueCount = input.issues.filter(
270
- (i) => i.status === "open"
270
+ this.activeIssueCount = input.issues.filter(
271
+ (i) => i.status !== "resolved"
271
272
  ).length;
272
273
  const bySev = /* @__PURE__ */ new Map();
273
274
  for (const i of input.issues) {
274
- if (i.status === "open") {
275
+ if (i.status !== "resolved") {
275
276
  bySev.set(i.severity, (bySev.get(i.severity) ?? 0) + 1);
276
277
  }
277
278
  }
@@ -288,6 +289,7 @@ var ProjectState = class _ProjectState {
288
289
  this.deprecatedLessonCount = this.lessons.filter(
289
290
  (l) => l.status === "deprecated" || l.status === "superseded"
290
291
  ).length;
292
+ this.lessonTags = [...new Set(this.lessons.flatMap((l) => l.tags ?? []))].sort();
291
293
  }
292
294
  // --- Query Methods ---
293
295
  isUmbrella(ticket) {
@@ -502,6 +504,20 @@ function extractHandoverDate(filename) {
502
504
  const match = filename.match(HANDOVER_DATE_REGEX);
503
505
  return match ? match[0] : null;
504
506
  }
507
+ function extractHandoverTitle(filename) {
508
+ let name = filename;
509
+ if (name.endsWith(".md")) {
510
+ name = name.slice(0, -3);
511
+ }
512
+ if (HANDOVER_DATE_REGEX.test(name)) {
513
+ let title = name.slice(10);
514
+ if (title.startsWith("-")) {
515
+ title = title.slice(1);
516
+ }
517
+ return title.replace(/-/g, " ").trim();
518
+ }
519
+ return name;
520
+ }
505
521
 
506
522
  // src/core/project-loader.ts
507
523
  async function loadProject(root, options) {
@@ -2095,6 +2111,75 @@ import { readdir as readdir3, readFile as readFile4, mkdir as mkdir3, unlink as
2095
2111
  import { existsSync as existsSync4 } from "fs";
2096
2112
  import { join as join5, resolve as resolve4 } from "path";
2097
2113
  import { z as z8 } from "zod";
2114
+
2115
+ // src/autonomous/git-inspector.ts
2116
+ import { execFile } from "child_process";
2117
+ var GIT_TIMEOUT = 1e4;
2118
+ async function git(cwd, args, parse) {
2119
+ return new Promise((resolve5) => {
2120
+ execFile("git", args, { cwd, timeout: GIT_TIMEOUT, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
2121
+ if (err) {
2122
+ const message = stderr?.trim() || err.message || "unknown git error";
2123
+ resolve5({ ok: false, reason: "git_error", message });
2124
+ return;
2125
+ }
2126
+ try {
2127
+ resolve5({ ok: true, data: parse(stdout) });
2128
+ } catch (parseErr) {
2129
+ resolve5({ ok: false, reason: "parse_error", message: parseErr.message });
2130
+ }
2131
+ });
2132
+ });
2133
+ }
2134
+ var SAFE_REF = /^[0-9a-f]{4,40}$/i;
2135
+ async function gitIsAncestor(cwd, ancestor, descendant) {
2136
+ if (!SAFE_REF.test(ancestor) || !SAFE_REF.test(descendant)) {
2137
+ return { ok: false, reason: "git_error", message: "invalid ref format" };
2138
+ }
2139
+ return new Promise((resolve5) => {
2140
+ execFile(
2141
+ "git",
2142
+ ["merge-base", "--is-ancestor", ancestor, descendant],
2143
+ { cwd, timeout: GIT_TIMEOUT },
2144
+ (err) => {
2145
+ if (!err) {
2146
+ resolve5({ ok: true, data: true });
2147
+ return;
2148
+ }
2149
+ const code = err.code;
2150
+ if (code === 1) {
2151
+ resolve5({ ok: true, data: false });
2152
+ return;
2153
+ }
2154
+ resolve5({ ok: false, reason: "git_error", message: err.message });
2155
+ }
2156
+ );
2157
+ });
2158
+ }
2159
+ async function gitHeadHash(cwd) {
2160
+ return new Promise((resolve5) => {
2161
+ execFile("git", ["rev-parse", "HEAD"], { cwd, timeout: 3e3 }, (err, stdout) => {
2162
+ if (err) {
2163
+ const message = err.stderr?.trim() || err.message || "unknown git error";
2164
+ resolve5({ ok: false, reason: "git_error", message });
2165
+ return;
2166
+ }
2167
+ resolve5({ ok: true, data: stdout.trim() });
2168
+ });
2169
+ });
2170
+ }
2171
+ async function gitCommitDistance(cwd, fromSha, toSha) {
2172
+ if (!SAFE_REF.test(fromSha) || !SAFE_REF.test(toSha)) {
2173
+ return { ok: false, reason: "git_error", message: "invalid ref format" };
2174
+ }
2175
+ return git(cwd, ["rev-list", "--count", `${fromSha}..${toSha}`], (out) => {
2176
+ const n = parseInt(out.trim(), 10);
2177
+ if (Number.isNaN(n)) throw new Error(`unexpected rev-list output: ${out}`);
2178
+ return n;
2179
+ });
2180
+ }
2181
+
2182
+ // src/core/snapshot.ts
2098
2183
  var LoadWarningSchema = z8.object({
2099
2184
  type: z8.string(),
2100
2185
  file: z8.string(),
@@ -2111,7 +2196,8 @@ var SnapshotV1Schema = z8.object({
2111
2196
  notes: z8.array(NoteSchema).optional().default([]),
2112
2197
  lessons: z8.array(LessonSchema).optional().default([]),
2113
2198
  handoverFilenames: z8.array(z8.string()).optional().default([]),
2114
- warnings: z8.array(LoadWarningSchema).optional()
2199
+ warnings: z8.array(LoadWarningSchema).optional(),
2200
+ gitHead: z8.string().optional()
2115
2201
  });
2116
2202
  var MAX_SNAPSHOTS = 20;
2117
2203
  async function saveSnapshot(root, loadResult) {
@@ -2140,6 +2226,10 @@ async function saveSnapshot(root, loadResult) {
2140
2226
  }))
2141
2227
  } : {}
2142
2228
  };
2229
+ const headResult = await gitHeadHash(absRoot);
2230
+ if (headResult.ok) {
2231
+ snapshot.gitHead = headResult.data;
2232
+ }
2143
2233
  const json = JSON.stringify(snapshot, null, 2) + "\n";
2144
2234
  const targetPath = join5(snapshotsDir, filename);
2145
2235
  const wrapDir = join5(absRoot, ".story");
@@ -2335,7 +2425,7 @@ function diffStates(snapshotState, currentState) {
2335
2425
  handovers: { added: handoversAdded, removed: handoversRemoved }
2336
2426
  };
2337
2427
  }
2338
- function buildRecap(currentState, snapshotInfo) {
2428
+ async function buildRecap(currentState, snapshotInfo, root) {
2339
2429
  const next = nextTicket(currentState);
2340
2430
  const nextTicketAction = next.kind === "found" ? { id: next.ticket.id, title: next.ticket.title, phase: next.ticket.phase } : null;
2341
2431
  const highSeverityIssues = currentState.issues.filter(
@@ -2365,6 +2455,30 @@ function buildRecap(currentState, snapshotInfo) {
2365
2455
  });
2366
2456
  const changes = diffStates(snapshotState, currentState);
2367
2457
  const recentlyClearedBlockers = changes.blockers.cleared;
2458
+ let staleness;
2459
+ if (snapshot.gitHead) {
2460
+ const currentHeadResult = await gitHeadHash(root);
2461
+ if (currentHeadResult.ok) {
2462
+ const snapshotSha = snapshot.gitHead;
2463
+ const currentSha = currentHeadResult.data;
2464
+ if (snapshotSha !== currentSha) {
2465
+ const ancestorResult = await gitIsAncestor(root, snapshotSha, currentSha);
2466
+ if (ancestorResult.ok && ancestorResult.data) {
2467
+ const distResult = await gitCommitDistance(root, snapshotSha, currentSha);
2468
+ if (distResult.ok) {
2469
+ staleness = {
2470
+ status: "behind",
2471
+ snapshotSha,
2472
+ currentSha,
2473
+ commitsBehind: distResult.data
2474
+ };
2475
+ }
2476
+ } else if (ancestorResult.ok && !ancestorResult.data) {
2477
+ staleness = { status: "diverged", snapshotSha, currentSha };
2478
+ }
2479
+ }
2480
+ }
2481
+ }
2368
2482
  return {
2369
2483
  snapshot: { filename, createdAt: snapshot.createdAt },
2370
2484
  changes,
@@ -2373,7 +2487,8 @@ function buildRecap(currentState, snapshotInfo) {
2373
2487
  highSeverityIssues,
2374
2488
  recentlyClearedBlockers
2375
2489
  },
2376
- partial: (snapshot.warnings ?? []).length > 0
2490
+ partial: (snapshot.warnings ?? []).length > 0,
2491
+ ...staleness ? { staleness } : {}
2377
2492
  };
2378
2493
  }
2379
2494
  function formatSnapshotFilename(date) {
@@ -2457,7 +2572,7 @@ function formatStatus(state, format, activeSessions = []) {
2457
2572
  completeTickets: state.completeLeafTicketCount,
2458
2573
  openTickets: state.leafTicketCount - state.completeLeafTicketCount,
2459
2574
  blockedTickets: state.blockedCount,
2460
- openIssues: state.openIssueCount,
2575
+ openIssues: state.activeIssueCount,
2461
2576
  activeNotes: state.activeNoteCount,
2462
2577
  archivedNotes: state.archivedNoteCount,
2463
2578
  activeLessons: state.activeLessonCount,
@@ -2479,7 +2594,7 @@ function formatStatus(state, format, activeSessions = []) {
2479
2594
  `# ${escapeMarkdownInline(state.config.project)}`,
2480
2595
  "",
2481
2596
  `Tickets: ${state.completeLeafTicketCount}/${state.leafTicketCount} complete, ${state.blockedCount} blocked`,
2482
- `Issues: ${state.openIssueCount} open`,
2597
+ `Issues: ${state.activeIssueCount} open`,
2483
2598
  `Notes: ${state.activeNoteCount} active, ${state.archivedNoteCount} archived`,
2484
2599
  `Lessons: ${state.activeLessonCount} active, ${state.deprecatedLessonCount} deprecated`,
2485
2600
  `Handovers: ${state.handoverFilenames.length}`,
@@ -2764,7 +2879,7 @@ function formatRecap(recap, state, format) {
2764
2879
  lines.push("No snapshot found. Run `claudestory snapshot` to enable session diffs.");
2765
2880
  lines.push("");
2766
2881
  lines.push(`Tickets: ${state.completeLeafTicketCount}/${state.leafTicketCount} complete, ${state.blockedCount} blocked`);
2767
- lines.push(`Issues: ${state.openIssueCount} open`);
2882
+ lines.push(`Issues: ${state.activeIssueCount} open`);
2768
2883
  } else {
2769
2884
  lines.push(`# ${escapeMarkdownInline(state.config.project)} \u2014 Recap`);
2770
2885
  lines.push("");
@@ -2772,6 +2887,13 @@ function formatRecap(recap, state, format) {
2772
2887
  if (recap.partial) {
2773
2888
  lines.push("**Note:** Snapshot was taken from a project with integrity warnings. Diff may be incomplete.");
2774
2889
  }
2890
+ if (recap.staleness) {
2891
+ if (recap.staleness.status === "diverged") {
2892
+ lines.push("**Warning:** Snapshot commit is not an ancestor of current HEAD (history diverged; possible rebase, force-push, or branch switch).");
2893
+ } else if (recap.staleness.status === "behind" && recap.staleness.commitsBehind) {
2894
+ lines.push(`**Warning:** Snapshot is ${recap.staleness.commitsBehind} commit(s) behind HEAD -- context may be stale.`);
2895
+ }
2896
+ }
2775
2897
  const changes = recap.changes;
2776
2898
  const hasChanges = hasAnyChanges(changes);
2777
2899
  if (!hasChanges) {
@@ -3033,7 +3155,7 @@ function formatFullExport(state, format) {
3033
3155
  lines.push(`# ${escapeMarkdownInline(state.config.project)} \u2014 Full Export`);
3034
3156
  lines.push("");
3035
3157
  lines.push(`Tickets: ${state.completeLeafTicketCount}/${state.leafTicketCount} complete`);
3036
- lines.push(`Issues: ${state.openIssueCount} open`);
3158
+ lines.push(`Issues: ${state.activeIssueCount} open`);
3037
3159
  lines.push(`Notes: ${state.activeNoteCount} active, ${state.archivedNoteCount} archived`);
3038
3160
  lines.push(`Lessons: ${state.activeLessonCount} active, ${state.deprecatedLessonCount} deprecated`);
3039
3161
  lines.push("");
@@ -3175,6 +3297,7 @@ export {
3175
3297
  errorEnvelope,
3176
3298
  escapeMarkdownInline,
3177
3299
  extractHandoverDate,
3300
+ extractHandoverTitle,
3178
3301
  fencedBlock,
3179
3302
  formatBlockedTickets,
3180
3303
  formatBlockerList,