rcf-lite 0.28.0 → 0.28.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +9 -0
  3. package/fixtures/canary-manifest.json +9 -9
  4. package/package.json +1 -1
  5. package/rcf/code-nodes/cn-600.json +2 -1
  6. package/rcf/code-nodes/cn-602.json +4 -2
  7. package/rcf/code-nodes/cn-605.json +2 -1
  8. package/rcf/fbs/fbs-181.json +4 -2
  9. package/rcf/fbs/fbs-182.json +2 -1
  10. package/rcf/tacs/tac-4103-feedback-redact.json +3 -2
  11. package/rcf/tacs/tac-4106-feedback-destination.json +3 -2
  12. package/rcf/test-suites/ts-188.json +18 -2
  13. package/rcf/test-suites/ts-189.json +24 -2
  14. package/rcf/test-suites/ts-191.json +19 -5
  15. package/rcf/user-stories/us-15601.json +19 -1
  16. package/rcf/user-stories/us-15701.json +4 -4
  17. package/rcf/user-stories/us-15801.json +10 -1
  18. package/releases/releases.yaml +21 -1
  19. package/src/browser-verify/manifest-writer.js +9 -5
  20. package/src/cli/browser-verify.js +9 -1
  21. package/src/cli/feedback.js +201 -24
  22. package/src/cli/intake.js +25 -4
  23. package/src/cli/preflight.js +24 -3
  24. package/src/cli/review.js +9 -1
  25. package/src/cli/ui-baseline.js +14 -1
  26. package/src/core/store/index.js +5 -0
  27. package/src/core/store/validate-composed-record.js +95 -0
  28. package/src/feedback/destination.js +98 -4
  29. package/src/feedback/gh.js +27 -1
  30. package/src/feedback/redact-allowlist.json +7 -0
  31. package/src/feedback/redact.js +75 -6
  32. package/src/feedback/render.js +34 -12
  33. package/src/intake/manifest-writer.js +6 -3
  34. package/src/intake/orchestrator.js +44 -7
  35. package/src/preflight/manifest-writer.js +11 -11
  36. package/src/req-baseline/sweep.js +34 -5
  37. package/src/ui-baseline/manifest-writer.js +11 -10
  38. package/src/view/doc-renderers/helpers.js +37 -0
  39. package/src/view/doc-renderers/tad.js +67 -6
  40. package/src/view/style.css +5 -0
@@ -77,6 +77,43 @@ export function fieldList(label, items) {
77
77
  return `<section class="field-list"><h4>${escapeHtml(label)}</h4><ul>${li}</ul></section>`;
78
78
  }
79
79
 
80
+ /**
81
+ * 0.28.2 (issue #235): render an array-of-object field as a HTML
82
+ * table with the schema-known columns. Empty columns are suppressed so
83
+ * schema-optional fields do not surface as blank columns. Every cell
84
+ * is escaped. Used by the TAD renderer for the three structured
85
+ * fields the TAD schema names (dataStores, coreEntities,
86
+ * externalSystems) so a review-surface reader sees a legible table
87
+ * instead of a single-line `JSON.stringify` dump.
88
+ *
89
+ * @param {string} label section heading
90
+ * @param {object[]} rows array of row objects (dropped when empty)
91
+ * @param {Array<{ key: string, label?: string }>} columns
92
+ * @returns {string}
93
+ */
94
+ export function fieldObjectTable(label, rows, columns) {
95
+ if (!Array.isArray(rows) || rows.length === 0) return '';
96
+ if (!Array.isArray(columns) || columns.length === 0) return '';
97
+ // Drop columns that are empty across every row so a schema-optional
98
+ // field (`purpose`, `description`, `protocol`) does not surface as
99
+ // a blank column.
100
+ const usedColumns = columns.filter((col) => rows.some((r) => {
101
+ const v = r?.[col.key];
102
+ return v !== undefined && v !== null && String(v).length > 0;
103
+ }));
104
+ if (usedColumns.length === 0) return '';
105
+ const thead = usedColumns.map((c) => `<th>${escapeHtml(c.label ?? c.key)}</th>`).join('');
106
+ const tbody = rows.map((r) => {
107
+ const cells = usedColumns.map((c) => {
108
+ const v = r?.[c.key];
109
+ const text = v === undefined || v === null ? '' : String(v);
110
+ return `<td>${escapeHtml(text)}</td>`;
111
+ }).join('');
112
+ return `<tr>${cells}</tr>`;
113
+ }).join('');
114
+ return `<section class="field-list"><h4>${escapeHtml(label)}</h4><table class="field-table"><thead><tr>${thead}</tr></thead><tbody>${tbody}</tbody></table></section>`;
115
+ }
116
+
80
117
  /**
81
118
  * Render the "Show raw JSON" disclosure block for a document.
82
119
  *
@@ -9,10 +9,34 @@ import {
9
9
  docLinkList,
10
10
  escapeHtml,
11
11
  fieldList,
12
+ fieldObjectTable,
12
13
  fieldPara,
13
14
  rawJsonDisclosure,
14
15
  } from './helpers.js';
15
16
 
17
+ // 0.28.2 (issue #235): structured fields the TAD schema explicitly
18
+ // types as arrays of objects. Each entry maps a field-key to the
19
+ // schema-documented column layout so `renderOptionalSections` can
20
+ // pick a table renderer over the raw-JSON dump the pre-fix code
21
+ // emitted. Columns follow @stravica-ai/rcf-schemas@0.6.3
22
+ // schemas/tad.schema.json.
23
+ const TAD_STRUCTURED_FIELDS = {
24
+ dataStores: [
25
+ { key: 'name', label: 'Name' },
26
+ { key: 'kind', label: 'Kind' },
27
+ { key: 'purpose', label: 'Purpose' },
28
+ ],
29
+ coreEntities: [
30
+ { key: 'name', label: 'Name' },
31
+ { key: 'description', label: 'Description' },
32
+ ],
33
+ externalSystems: [
34
+ { key: 'name', label: 'Name' },
35
+ { key: 'purpose', label: 'Purpose' },
36
+ { key: 'protocol', label: 'Protocol' },
37
+ ],
38
+ };
39
+
16
40
  /**
17
41
  * @param {object} tad
18
42
  * @param {object} ctx
@@ -63,12 +87,49 @@ function renderOptionalSections(tad) {
63
87
  for (const [key, label] of Object.entries(named)) {
64
88
  const section = tad[key];
65
89
  if (!section || typeof section !== 'object') continue;
66
- const rows = Object.entries(section).map(([k, v]) => {
67
- if (typeof v === 'string') return `<dt>${escapeHtml(k)}</dt><dd>${escapeHtml(v)}</dd>`;
68
- if (Array.isArray(v)) return `<dt>${escapeHtml(k)}</dt><dd>${v.map((x) => `<code>${escapeHtml(typeof x === 'object' ? JSON.stringify(x) : x)}</code>`).join(', ')}</dd>`;
69
- return `<dt>${escapeHtml(k)}</dt><dd><pre>${escapeHtml(JSON.stringify(v, null, 2))}</pre></dd>`;
70
- }).join('\n');
71
- out.push(`<section class="field-list"><h4>${escapeHtml(label)}</h4><dl>${rows}</dl></section>`);
90
+ // 0.28.2 (issue #235): pull the schema-known structured fields
91
+ // (dataStores, coreEntities, externalSystems) out of the generic
92
+ // dl loop and render them as tables. The remaining scalar and
93
+ // array-of-string fields keep their dl treatment.
94
+ const structuredBlocks = [];
95
+ const dlEntries = [];
96
+ for (const [k, v] of Object.entries(section)) {
97
+ if (TAD_STRUCTURED_FIELDS[k] && Array.isArray(v) && v.length > 0 && v.every((x) => x && typeof x === 'object')) {
98
+ const table = fieldObjectTable(prettyStructuredLabel(k), v, TAD_STRUCTURED_FIELDS[k]);
99
+ if (table) structuredBlocks.push(table);
100
+ continue;
101
+ }
102
+ if (typeof v === 'string') {
103
+ dlEntries.push(`<dt>${escapeHtml(k)}</dt><dd>${escapeHtml(v)}</dd>`);
104
+ continue;
105
+ }
106
+ if (Array.isArray(v)) {
107
+ // Array-of-string keeps the existing inline shape. An
108
+ // unknown array-of-object surface falls through to a nested
109
+ // disclosure so it stays legible even when the schema grows
110
+ // a new field before this renderer knows about it.
111
+ if (v.every((x) => typeof x !== 'object' || x === null)) {
112
+ dlEntries.push(`<dt>${escapeHtml(k)}</dt><dd>${v.map((x) => `<code>${escapeHtml(typeof x === 'object' ? JSON.stringify(x) : x)}</code>`).join(', ')}</dd>`);
113
+ } else {
114
+ const items = v.map((x) => `<li><pre>${escapeHtml(JSON.stringify(x, null, 2))}</pre></li>`).join('');
115
+ dlEntries.push(`<dt>${escapeHtml(k)}</dt><dd><ul>${items}</ul></dd>`);
116
+ }
117
+ continue;
118
+ }
119
+ dlEntries.push(`<dt>${escapeHtml(k)}</dt><dd><pre>${escapeHtml(JSON.stringify(v, null, 2))}</pre></dd>`);
120
+ }
121
+ const dl = dlEntries.length > 0 ? `<dl>${dlEntries.join('\n')}</dl>` : '';
122
+ const structured = structuredBlocks.join('\n');
123
+ out.push(`<section class="field-list"><h4>${escapeHtml(label)}</h4>${dl}${structured}</section>`);
72
124
  }
73
125
  return out.join('\n');
74
126
  }
127
+
128
+ function prettyStructuredLabel(key) {
129
+ const map = {
130
+ dataStores: 'Data stores',
131
+ coreEntities: 'Core entities',
132
+ externalSystems: 'External systems',
133
+ };
134
+ return map[key] ?? key;
135
+ }
@@ -294,6 +294,11 @@ article.doc h5 { margin: 0.6rem 0 0.3rem; font-size: 0.925rem; font-weight: 600;
294
294
  .field-list ul li { margin: 0.2rem 0; }
295
295
  .field-list dl dt { font-weight: 600; margin-top: 0.5rem; color: var(--colour-heading); }
296
296
  .field-list dl dd { margin-left: 0; margin-bottom: 0.35rem; }
297
+ /* 0.28.2 (issue #235): structured TAD fields render as a table. */
298
+ .field-list table.field-table { border-collapse: collapse; width: 100%; margin: 0.35rem 0 0.85rem; font-size: 0.95em; }
299
+ .field-list table.field-table th, .field-list table.field-table td { text-align: left; padding: 0.35rem 0.55rem; border-bottom: 1px solid var(--colour-rule, rgba(0,0,0,0.12)); vertical-align: top; }
300
+ .field-list table.field-table th { font-weight: 600; color: var(--colour-heading); background: var(--colour-tint, rgba(0,0,0,0.03)); }
301
+ .field-list table.field-table tr:last-child td { border-bottom: 0; }
297
302
 
298
303
  /* --------------------------------------------------------------------
299
304
  * Acceptance criteria list (inside a US)