syntaur 0.46.0 → 0.48.0

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.
Files changed (72) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dashboard/dist/assets/{_basePickBy-RQBuJKcX.js → _basePickBy-Cie7FXcV.js} +1 -1
  3. package/dashboard/dist/assets/{_baseUniq-_J7s4kD3.js → _baseUniq-C2af7-lW.js} +1 -1
  4. package/dashboard/dist/assets/{arc-_9SyUgKQ.js → arc-BHKGjEv8.js} +1 -1
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-C8LeFMgr.js → architectureDiagram-2XIMDMQ5-CWqzcG1t.js} +1 -1
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-gMh0EPEh.js → blockDiagram-WCTKOSBZ-CHLC0M63.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-cHwecwLI.js → c4Diagram-IC4MRINW-Bx6GTZzJ.js} +1 -1
  8. package/dashboard/dist/assets/channel-CXlttLwe.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-Bb2anYuQ.js → chunk-4BX2VUAB-7cTgwG6c.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-55IACEB6-DYIRGzA1.js → chunk-55IACEB6-DQmdA20e.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-sgRWBbaF.js → chunk-FMBD7UC4-knmOnIKI.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-JSJVCQXG-DlYKMl_j.js → chunk-JSJVCQXG-CaiLHJFn.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-KX2RTZJC-D0YDLAOF.js → chunk-KX2RTZJC-Crp4zWuY.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-D-Y-CUx6.js → chunk-NQ4KR5QH-DDjtdNaK.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-D7FpSvb5.js → chunk-QZHKN3VN-Cbm1IieP.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-CtXgQLdS.js → chunk-WL4C6EOR-CdrRh6R4.js} +1 -1
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-BJM0Dprk.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BJM0Dprk.js +1 -0
  19. package/dashboard/dist/assets/clone-BTq7ueDt.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-YbTaohoJ.js → cose-bilkent-S5V4N54A-DyNajnMF.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-CMtwGAnP.js → dagre-KLK3FWXG-CIBjpfqE.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-E7M64L7V-D8wBMBAX.js → diagram-E7M64L7V-CDENlpZN.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-IFDJBPK2-DfudLpiJ.js → diagram-IFDJBPK2-CIGMJoFB.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-CyMy61wE.js → diagram-P4PSJMXO-C27fj5wp.js} +1 -1
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-BlB4ZQl9.js → erDiagram-INFDFZHY-M2CywJym.js} +1 -1
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-DbhDQJM3.js → flowDiagram-PKNHOUZH-CTV6ROYf.js} +1 -1
  27. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-DJFqteNi.js → ganttDiagram-A5KZAMGK-CxH3f3e5.js} +1 -1
  28. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-D8etA_mm.js → gitGraphDiagram-K3NZZRJ6-CwhnVvHE.js} +1 -1
  29. package/dashboard/dist/assets/{graph-Ce86jeZn.js → graph-DGkI_ekZ.js} +1 -1
  30. package/dashboard/dist/assets/{index-DzHQIE2n.css → index-BxO2I5dN.css} +1 -1
  31. package/dashboard/dist/assets/index-CTdFARW9.js +567 -0
  32. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-Cx35U-h8.js → infoDiagram-LFFYTUFH-CuKReAi2.js} +1 -1
  33. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-C04Y2nj8.js → ishikawaDiagram-PHBUUO56-DyBMt5Mw.js} +1 -1
  34. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-D8-cxbxE.js → journeyDiagram-4ABVD52K-CnwtbTNs.js} +1 -1
  35. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-DVKqMylP.js → kanban-definition-K7BYSVSG-5Sh1bC5j.js} +1 -1
  36. package/dashboard/dist/assets/{layout-98xZDpgu.js → layout-W2Vytcip.js} +1 -1
  37. package/dashboard/dist/assets/{linear-0jk_IwAc.js → linear-UTm9E6c4.js} +1 -1
  38. package/dashboard/dist/assets/{mermaid.core-C337VWfr.js → mermaid.core-B0EB-dBk.js} +4 -4
  39. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-8sNYGYEP.js → mindmap-definition-YRQLILUH-CH4L08eg.js} +1 -1
  40. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-afcmzHxf.js → pieDiagram-SKSYHLDU-CQ3PMl-w.js} +1 -1
  41. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-B4RjcpOq.js → quadrantDiagram-337W2JSQ-DbZgXUm5.js} +1 -1
  42. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-CRavU6cI.js → requirementDiagram-Z7DCOOCP-CxJWytQa.js} +1 -1
  43. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-DFomU3z-.js → sankeyDiagram-WA2Y5GQK-3YeEasvh.js} +1 -1
  44. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CGKO7nmK.js → sequenceDiagram-2WXFIKYE-F2EyvYsW.js} +1 -1
  45. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-BjFI1K8h.js → stateDiagram-RAJIS63D-DrmiemaD.js} +1 -1
  46. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DfyvP5EF.js +1 -0
  47. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-BBo8XJFG.js → timeline-definition-YZTLITO2-CBee2YiE.js} +1 -1
  48. package/dashboard/dist/assets/{treemap-KZPCXAKY-COd6i6TE.js → treemap-KZPCXAKY-BkPRIS3K.js} +1 -1
  49. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-CGQweQ36.js → vennDiagram-LZ73GAT5-CTsfwv8G.js} +1 -1
  50. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-mfJ5So7N.js → xychartDiagram-JWTSCODW-DUSo-c6w.js} +1 -1
  51. package/dashboard/dist/index.html +2 -2
  52. package/dist/dashboard/server.js +188 -73
  53. package/dist/dashboard/server.js.map +1 -1
  54. package/dist/index.js +198 -74
  55. package/dist/index.js.map +1 -1
  56. package/dist/launch/index.d.ts +25 -12
  57. package/dist/launch/index.js +62 -58
  58. package/dist/launch/index.js.map +1 -1
  59. package/package.json +1 -1
  60. package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
  61. package/platforms/claude-code/skills/manage-statuses/SKILL.md +7 -0
  62. package/platforms/codex/.codex-plugin/plugin.json +1 -1
  63. package/platforms/codex/skills/manage-statuses/SKILL.md +7 -0
  64. package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
  65. package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
  66. package/skills/manage-statuses/SKILL.md +7 -0
  67. package/dashboard/dist/assets/channel-C36dnl_e.js +0 -1
  68. package/dashboard/dist/assets/classDiagram-VBA2DB6C-BsoGa6_a.js +0 -1
  69. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BsoGa6_a.js +0 -1
  70. package/dashboard/dist/assets/clone-Bz6jW3OY.js +0 -1
  71. package/dashboard/dist/assets/index-DRng26Jg.js +0 -567
  72. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-BtxefYKD.js +0 -1
@@ -3046,6 +3046,53 @@ function factFieldNames(decl) {
3046
3046
  ].map((k) => k.toLowerCase()) : [exportNames.fact.toLowerCase()];
3047
3047
  return { storageKey: name, exports: exportNames, registryKeys };
3048
3048
  }
3049
+ function validateFactDeclarations(raw2) {
3050
+ const problems = [];
3051
+ const owners = /* @__PURE__ */ new Map();
3052
+ for (const key of Object.keys(DERIVE_FIELDS)) owners.set(key, "built-in");
3053
+ for (const key of Object.keys(ASSIGNMENT_FIELDS)) owners.set(key, "built-in");
3054
+ for (const row of raw2 ?? []) {
3055
+ const name = (row?.name ?? "").trim();
3056
+ if (!/^[a-z][a-zA-Z0-9]*$/.test(name)) {
3057
+ problems.push(
3058
+ `fact "${row?.name ?? ""}": invalid name \u2014 must match /^[a-z][a-zA-Z0-9]*$/`
3059
+ );
3060
+ continue;
3061
+ }
3062
+ const type = (row.type ?? "").trim();
3063
+ if (type !== "bool" && type !== "number" && type !== "attestation") {
3064
+ problems.push(
3065
+ `fact "${name}": invalid type "${row.type ?? ""}" \u2014 expected bool, number, or attestation`
3066
+ );
3067
+ continue;
3068
+ }
3069
+ if (type === "attestation") {
3070
+ const binds = (row.binds ?? "none").toString().trim() || "none";
3071
+ if (binds !== "plan" && binds !== "commit" && binds !== "none") {
3072
+ problems.push(
3073
+ `fact "${name}": invalid binds "${row.binds}" \u2014 expected plan, commit, or none`
3074
+ );
3075
+ continue;
3076
+ }
3077
+ }
3078
+ const decl = type === "attestation" ? { name, type, binds: "none" } : { name, type };
3079
+ const keys = factFieldNames(decl).registryKeys;
3080
+ const collidingKey = keys.find((k) => owners.has(k));
3081
+ if (collidingKey !== void 0) {
3082
+ const owner = owners.get(collidingKey);
3083
+ if (owner === "built-in") {
3084
+ problems.push(`fact "${name}": exported field "${collidingKey}" collides with a built-in field`);
3085
+ } else if (owner === name) {
3086
+ problems.push(`fact "${name}": duplicate declaration (a fact named "${name}" is already declared)`);
3087
+ } else {
3088
+ problems.push(`fact "${name}": exported field "${collidingKey}" collides with fact "${owner}"`);
3089
+ }
3090
+ continue;
3091
+ }
3092
+ for (const key of keys) owners.set(key, name);
3093
+ }
3094
+ return problems;
3095
+ }
3049
3096
  function acceptFactDeclarations(declarations) {
3050
3097
  const taken = /* @__PURE__ */ new Set([
3051
3098
  ...Object.keys(DERIVE_FIELDS),
@@ -3571,7 +3618,6 @@ function parseStatusConfig(content) {
3571
3618
  continue;
3572
3619
  }
3573
3620
  }
3574
- if (statuses.length === 0) return null;
3575
3621
  const derive = phaseLadder.length > 0 || disposition.length > 0 || Object.keys(headline).length > 0 ? {
3576
3622
  phaseLadder: phaseLadder.length > 0 ? phaseLadder : DEFAULT_DERIVE_CONFIG.phaseLadder,
3577
3623
  disposition: disposition.length > 0 ? disposition : DEFAULT_DERIVE_CONFIG.disposition,
@@ -3582,6 +3628,7 @@ function parseStatusConfig(content) {
3582
3628
  active: "phase"
3583
3629
  }
3584
3630
  } : null;
3631
+ if (statuses.length === 0 && facts.length === 0 && derive === null) return null;
3585
3632
  return {
3586
3633
  statuses,
3587
3634
  order: order.length > 0 ? order : statuses.map((s) => s.id),
@@ -3707,53 +3754,6 @@ function validateDeriveConfig(derive, statusConfig, validateWhen = () => null) {
3707
3754
  }
3708
3755
  return problems;
3709
3756
  }
3710
- function validateFactDeclarations(raw2) {
3711
- const problems = [];
3712
- const owners = /* @__PURE__ */ new Map();
3713
- for (const key of Object.keys(DERIVE_FIELDS)) owners.set(key, "built-in");
3714
- for (const key of Object.keys(ASSIGNMENT_FIELDS)) owners.set(key, "built-in");
3715
- for (const row of raw2 ?? []) {
3716
- const name = (row?.name ?? "").trim();
3717
- if (!/^[a-z][a-zA-Z0-9]*$/.test(name)) {
3718
- problems.push(
3719
- `fact "${row?.name ?? ""}": invalid name \u2014 must match /^[a-z][a-zA-Z0-9]*$/`
3720
- );
3721
- continue;
3722
- }
3723
- const type = (row.type ?? "").trim();
3724
- if (type !== "bool" && type !== "number" && type !== "attestation") {
3725
- problems.push(
3726
- `fact "${name}": invalid type "${row.type ?? ""}" \u2014 expected bool, number, or attestation`
3727
- );
3728
- continue;
3729
- }
3730
- if (type === "attestation") {
3731
- const binds = (row.binds ?? "none").toString().trim() || "none";
3732
- if (binds !== "plan" && binds !== "commit" && binds !== "none") {
3733
- problems.push(
3734
- `fact "${name}": invalid binds "${row.binds}" \u2014 expected plan, commit, or none`
3735
- );
3736
- continue;
3737
- }
3738
- }
3739
- const decl = type === "attestation" ? { name, type, binds: "none" } : { name, type };
3740
- const keys = factFieldNames(decl).registryKeys;
3741
- const collidingKey = keys.find((k) => owners.has(k));
3742
- if (collidingKey !== void 0) {
3743
- const owner = owners.get(collidingKey);
3744
- if (owner === "built-in") {
3745
- problems.push(`fact "${name}": exported field "${collidingKey}" collides with a built-in field`);
3746
- } else if (owner === name) {
3747
- problems.push(`fact "${name}": duplicate declaration (a fact named "${name}" is already declared)`);
3748
- } else {
3749
- problems.push(`fact "${name}": exported field "${collidingKey}" collides with fact "${owner}"`);
3750
- }
3751
- continue;
3752
- }
3753
- for (const key of keys) owners.set(key, name);
3754
- }
3755
- return problems;
3756
- }
3757
3757
  function serializeIntegrationConfig(integrations) {
3758
3758
  const lines = [];
3759
3759
  if (integrations.claudePluginDir) {
@@ -4833,7 +4833,6 @@ var init_config2 = __esm({
4833
4833
  init_agents_schema();
4834
4834
  init_slug();
4835
4835
  init_fact_registry();
4836
- init_query();
4837
4836
  init_terminal_schema();
4838
4837
  init_workspace_visibility_schema();
4839
4838
  DEFAULT_DERIVE_CONFIG = {
@@ -6061,7 +6060,8 @@ function rowToSession(row) {
6061
6060
  transcriptPath: row.transcript_path ?? null,
6062
6061
  pid: row.pid ?? null,
6063
6062
  pidStartedAt: row.pid_started_at ?? null,
6064
- originalHeadSha: row.original_head_sha ?? null
6063
+ originalHeadSha: row.original_head_sha ?? null,
6064
+ updatedAt: row.updated_at ?? null
6065
6065
  };
6066
6066
  }
6067
6067
  async function appendSession(_projectDir, session, opts) {
@@ -7452,24 +7452,28 @@ async function getStatusConfig() {
7452
7452
  if (_cachedConfig) return _cachedConfig;
7453
7453
  const config = await readConfig();
7454
7454
  if (config.statuses) {
7455
+ const sc = config.statuses;
7456
+ const defaults = sc.statuses.length === 0 ? buildDefaultStatusConfig() : null;
7457
+ const effectiveStatuses = defaults ? defaults.statuses : sc.statuses;
7458
+ const effectiveOrder = defaults ? defaults.order : sc.order;
7455
7459
  const terminalSet = new Set(
7456
- config.statuses.statuses.filter((s) => s.terminal).map((s) => s.id)
7460
+ effectiveStatuses.filter((s) => s.terminal).map((s) => s.id)
7457
7461
  );
7458
- const hasCustomTransitions = config.statuses.transitions.length > 0;
7459
- const effectiveTransitions = hasCustomTransitions ? config.statuses.transitions : Array.from(DEFAULT_TRANSITION_TABLE.entries()).map(([key, to]) => {
7462
+ const hasCustomTransitions = sc.transitions.length > 0;
7463
+ const effectiveTransitions = hasCustomTransitions ? sc.transitions : Array.from(DEFAULT_TRANSITION_TABLE.entries()).map(([key, to]) => {
7460
7464
  const [from, command] = key.split(":");
7461
7465
  return { from, command, to };
7462
7466
  });
7463
- const accepted = acceptFactDeclarations(normalizeFactDeclarations(config.statuses.facts ?? null));
7467
+ const accepted = acceptFactDeclarations(normalizeFactDeclarations(sc.facts ?? null));
7464
7468
  _cachedConfig = {
7465
7469
  custom: true,
7466
- statuses: config.statuses.statuses,
7467
- order: config.statuses.order,
7470
+ statuses: effectiveStatuses,
7471
+ order: effectiveOrder,
7468
7472
  transitions: effectiveTransitions,
7469
7473
  transitionTable: buildTransitionTable(effectiveTransitions),
7470
7474
  terminalStatuses: terminalSet.size > 0 ? terminalSet : /* @__PURE__ */ new Set(["completed", "failed"]),
7471
- derive: config.statuses.derive ?? null,
7472
- facts: config.statuses.facts ?? null,
7475
+ derive: sc.derive ?? null,
7476
+ facts: sc.facts ?? null,
7473
7477
  factDeclarations: accepted,
7474
7478
  deriveRegistry: buildDeriveRegistry(accepted),
7475
7479
  queryRegistry: buildQueryRegistry(accepted)
@@ -8633,7 +8637,7 @@ async function buildDerivedDetail(assignment, assignmentDir, projectDir) {
8633
8637
  try {
8634
8638
  const { computeFactsDetailed: computeFactsDetailed2 } = await Promise.resolve().then(() => (init_facts(), facts_exports));
8635
8639
  const { deriveDimensions: deriveDimensions2 } = await Promise.resolve().then(() => (init_derive(), derive_exports));
8636
- const { DEFAULT_DERIVE_CONFIG: DEFAULT_DERIVE_CONFIG2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
8640
+ const { DEFAULT_DERIVE_CONFIG: DEFAULT_DERIVE_CONFIG3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
8637
8641
  const { facts, attestations } = await computeFactsDetailed2({
8638
8642
  assignmentDir,
8639
8643
  frontmatter: {
@@ -8648,7 +8652,7 @@ async function buildDerivedDetail(assignment, assignmentDir, projectDir) {
8648
8652
  });
8649
8653
  const dims = deriveDimensions2({
8650
8654
  facts,
8651
- derive: config.derive ?? DEFAULT_DERIVE_CONFIG2,
8655
+ derive: config.derive ?? DEFAULT_DERIVE_CONFIG3,
8652
8656
  currentStatus: assignment.status,
8653
8657
  terminalStatuses: config.terminalStatuses,
8654
8658
  knownStatusIds: new Set(config.statuses.map((s) => s.id)),
@@ -11221,7 +11225,11 @@ var SORT_FIELDS = [
11221
11225
  "assignee",
11222
11226
  "dependencies",
11223
11227
  "created",
11224
- "updated"
11228
+ "updated",
11229
+ "started",
11230
+ "lastActivity",
11231
+ "projectName",
11232
+ "agentName"
11225
11233
  ];
11226
11234
  var SORT_DIRECTIONS = ["asc", "desc"];
11227
11235
  var DENSITIES = ["comfortable", "compact"];
@@ -11234,7 +11242,7 @@ var GROUPINGS = [
11234
11242
  "project"
11235
11243
  ];
11236
11244
  var ACTIVITIES = ["all", "stale", "fresh"];
11237
- var DATE_RANGE_FIELDS = ["created", "updated"];
11245
+ var DATE_RANGE_FIELDS = ["created", "updated", "started"];
11238
11246
  var DATE_RANGE_PRESETS = [
11239
11247
  "last_24h",
11240
11248
  "last_7d",
@@ -11483,6 +11491,7 @@ function makeSeededView(id, name, filterOverrides) {
11483
11491
  id,
11484
11492
  name,
11485
11493
  workspace: null,
11494
+ entityType: "assignment",
11486
11495
  config: {
11487
11496
  viewMode: "list",
11488
11497
  filters: { ...DEFAULT_FILTERS, ...filterOverrides },
@@ -11520,7 +11529,13 @@ function isWidgetConfig(value) {
11520
11529
  if (obj.kind === "saved-view") {
11521
11530
  return typeof obj.viewId === "string" && obj.viewId.length > 0;
11522
11531
  }
11523
- return obj.kind === "agent-sessions" || obj.kind === "inventories";
11532
+ if (obj.kind === "agent-sessions") {
11533
+ if (obj.viewId !== void 0) {
11534
+ return typeof obj.viewId === "string" && obj.viewId.length > 0;
11535
+ }
11536
+ return true;
11537
+ }
11538
+ return obj.kind === "inventories";
11524
11539
  }
11525
11540
  function isListSectionVisibility(value) {
11526
11541
  if (!value || typeof value !== "object") return false;
@@ -11557,6 +11572,9 @@ function isSavedViewConfig(value) {
11557
11572
  function isSavedView(value) {
11558
11573
  if (!value || typeof value !== "object") return false;
11559
11574
  const obj = value;
11575
+ if (obj.entityType !== void 0 && obj.entityType !== "assignment" && obj.entityType !== "session") {
11576
+ return false;
11577
+ }
11560
11578
  return typeof obj.id === "string" && obj.id.length > 0 && typeof obj.name === "string" && (obj.workspace === null || typeof obj.workspace === "string") && isSavedViewConfig(obj.config) && typeof obj.createdAt === "string" && typeof obj.updatedAt === "string";
11561
11579
  }
11562
11580
  function isDashboardSlot(value) {
@@ -11651,6 +11669,8 @@ function createSavedView(file, input, now = () => (/* @__PURE__ */ new Date()).t
11651
11669
  id: randomUUID(),
11652
11670
  name: input.name,
11653
11671
  workspace: input.workspace,
11672
+ // Only persist entityType when explicitly a session view; absent === assignment.
11673
+ ...input.entityType === "session" ? { entityType: "session" } : {},
11654
11674
  config: input.config,
11655
11675
  createdAt: ts,
11656
11676
  updatedAt: ts
@@ -11749,12 +11769,16 @@ function validateCreateBody(body) {
11749
11769
  if (!isSavedViewConfig(obj.config)) {
11750
11770
  return { ok: false, error: "config must be a valid SavedViewConfig" };
11751
11771
  }
11772
+ if (obj.entityType !== void 0 && obj.entityType !== "assignment" && obj.entityType !== "session") {
11773
+ return { ok: false, error: "entityType must be 'assignment' or 'session'" };
11774
+ }
11752
11775
  return {
11753
11776
  ok: true,
11754
11777
  value: {
11755
11778
  name: obj.name.trim(),
11756
11779
  workspace: obj.workspace,
11757
- config: obj.config
11780
+ config: obj.config,
11781
+ ...obj.entityType !== void 0 ? { entityType: obj.entityType } : {}
11758
11782
  }
11759
11783
  };
11760
11784
  }
@@ -16692,7 +16716,9 @@ function createWorkspaceVisibilityConfigRouter() {
16692
16716
 
16693
16717
  // src/dashboard/api-status-config.ts
16694
16718
  init_config2();
16719
+ init_fact_registry();
16695
16720
  init_api();
16721
+ init_derive();
16696
16722
  import { Router as Router9 } from "express";
16697
16723
 
16698
16724
  // src/utils/status-config-resolution.ts
@@ -17069,7 +17095,8 @@ function createStatusConfigRouter(projectsDir, assignmentsDir2) {
17069
17095
  order: config.order,
17070
17096
  transitions: config.transitions,
17071
17097
  custom: config.custom,
17072
- factDeclarations: config.factDeclarations
17098
+ factDeclarations: config.factDeclarations,
17099
+ rawFacts: config.facts ?? []
17073
17100
  });
17074
17101
  } catch (error) {
17075
17102
  console.error("Error getting status config:", error);
@@ -17093,8 +17120,27 @@ function createStatusConfigRouter(projectsDir, assignmentsDir2) {
17093
17120
  });
17094
17121
  router.post("/", async (req, res) => {
17095
17122
  try {
17096
- const { statuses, order, transitions, resolutions: rawResolutions } = req.body ?? {};
17097
- if (!Array.isArray(statuses) || !Array.isArray(order) || !Array.isArray(transitions)) {
17123
+ const {
17124
+ statuses,
17125
+ order,
17126
+ transitions,
17127
+ facts: bodyFacts,
17128
+ factRemovalAcks,
17129
+ resolutions: rawResolutions
17130
+ } = req.body ?? {};
17131
+ const currentConfig = await getStatusConfig();
17132
+ const hasFacts = bodyFacts !== void 0;
17133
+ const hasStatuses = statuses !== void 0;
17134
+ const hasOrder = order !== void 0;
17135
+ const hasTransitions = transitions !== void 0;
17136
+ let effectiveStatuses = statuses;
17137
+ let effectiveOrder = order;
17138
+ let effectiveTransitions = transitions;
17139
+ if (hasFacts && !hasStatuses && !hasOrder && !hasTransitions) {
17140
+ effectiveStatuses = currentConfig.statuses;
17141
+ effectiveOrder = currentConfig.order;
17142
+ effectiveTransitions = currentConfig.transitions;
17143
+ } else if (!Array.isArray(effectiveStatuses) || !Array.isArray(effectiveOrder) || !Array.isArray(effectiveTransitions)) {
17098
17144
  res.status(400).json({ error: "malformed-statuses", message: "Request body must include statuses, order, and transitions arrays" });
17099
17145
  return;
17100
17146
  }
@@ -17108,10 +17154,9 @@ function createStatusConfigRouter(projectsDir, assignmentsDir2) {
17108
17154
  return;
17109
17155
  }
17110
17156
  const resolutions = parsed.resolutions;
17111
- const currentConfig = await getStatusConfig();
17112
17157
  const oldIds = new Set(currentConfig.statuses.map((s) => s.id));
17113
17158
  const newIds = /* @__PURE__ */ new Set();
17114
- for (const s of statuses) {
17159
+ for (const s of effectiveStatuses) {
17115
17160
  if (s && typeof s === "object" && isString(s.id)) {
17116
17161
  newIds.add(s.id);
17117
17162
  }
@@ -17192,13 +17237,83 @@ function createStatusConfigRouter(projectsDir, assignmentsDir2) {
17192
17237
  }
17193
17238
  throw err;
17194
17239
  }
17240
+ let factsToWrite = currentConfig.facts ?? null;
17241
+ if (hasFacts) {
17242
+ const shapedFacts = [];
17243
+ if (!Array.isArray(bodyFacts)) {
17244
+ res.status(400).json({ error: "malformed-facts", message: "facts must be an array" });
17245
+ return;
17246
+ }
17247
+ for (let i = 0; i < bodyFacts.length; i++) {
17248
+ const row = bodyFacts[i];
17249
+ if (!row || typeof row !== "object") {
17250
+ res.status(400).json({ error: "malformed-facts", message: `facts[${i}] must be an object` });
17251
+ return;
17252
+ }
17253
+ const name = row.name;
17254
+ const type = row.type;
17255
+ if (typeof name !== "string" || typeof type !== "string") {
17256
+ res.status(400).json({ error: "malformed-facts", message: `facts[${i}] must have name and type strings` });
17257
+ return;
17258
+ }
17259
+ const binds = row.binds;
17260
+ const normalizedBinds = binds === void 0 ? null : binds === null ? null : typeof binds === "string" ? binds : null;
17261
+ shapedFacts.push({ name, type, binds: normalizedBinds });
17262
+ }
17263
+ const problems = validateFactDeclarations(shapedFacts);
17264
+ if (problems.length > 0) {
17265
+ res.status(400).json({ error: "invalid-facts", problems });
17266
+ return;
17267
+ }
17268
+ const currentNames = new Set((currentConfig.facts ?? []).map((f) => f.name));
17269
+ const incomingNames = new Set(shapedFacts.map((f) => f.name));
17270
+ const removedNames = [];
17271
+ for (const name of currentNames) {
17272
+ if (!incomingNames.has(name)) removedNames.push(name);
17273
+ }
17274
+ const acks = new Set(Array.isArray(factRemovalAcks) ? factRemovalAcks.map((x) => String(x)) : []);
17275
+ const unresolvedRefs = [];
17276
+ const deriveConfig = currentConfig.derive ?? null;
17277
+ if (deriveConfig !== null && removedNames.length > 0) {
17278
+ const acceptedAll = acceptFactDeclarations(normalizeFactDeclarations(currentConfig.facts));
17279
+ const fullRegistry = buildDeriveRegistry(acceptedAll);
17280
+ for (const removedName of removedNames) {
17281
+ if (acks.has(removedName)) continue;
17282
+ const acceptedWithout = acceptedAll.filter((d) => d.name !== removedName);
17283
+ const withoutRegistry = buildDeriveRegistry(acceptedWithout);
17284
+ for (let i = 0; i < deriveConfig.phaseLadder.length; i++) {
17285
+ const rung = deriveConfig.phaseLadder[i];
17286
+ if (rung.when === "*") continue;
17287
+ const before = validateDeriveCondition(rung.when, fullRegistry);
17288
+ const after = validateDeriveCondition(rung.when, withoutRegistry);
17289
+ if (before === null && after !== null) {
17290
+ unresolvedRefs.push({ factName: removedName, location: `phaseLadder[${i}]`, when: rung.when });
17291
+ }
17292
+ }
17293
+ for (let i = 0; i < deriveConfig.disposition.length; i++) {
17294
+ const rule = deriveConfig.disposition[i];
17295
+ if (rule.when === null) continue;
17296
+ const before = validateDeriveCondition(rule.when, fullRegistry);
17297
+ const after = validateDeriveCondition(rule.when, withoutRegistry);
17298
+ if (before === null && after !== null) {
17299
+ unresolvedRefs.push({ factName: removedName, location: `disposition[${i}]`, when: rule.when });
17300
+ }
17301
+ }
17302
+ }
17303
+ }
17304
+ if (unresolvedRefs.length > 0) {
17305
+ res.status(409).json({ error: "unresolved-fact-references", references: unresolvedRefs });
17306
+ return;
17307
+ }
17308
+ factsToWrite = shapedFacts;
17309
+ }
17195
17310
  try {
17196
17311
  await writeStatusConfig({
17197
- statuses,
17198
- order,
17199
- transitions,
17312
+ statuses: effectiveStatuses,
17313
+ order: effectiveOrder,
17314
+ transitions: effectiveTransitions,
17200
17315
  derive: currentConfig.derive ?? null,
17201
- facts: currentConfig.facts ?? null
17316
+ facts: factsToWrite
17202
17317
  });
17203
17318
  } catch (err) {
17204
17319
  console.error("Error saving status config after applying resolutions:", err);