cabloy 5.1.200 → 5.1.201
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/.cabloy-version +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +6 -0
- package/package.json +1 -1
- package/repo-agent-governance/managed-assets.json +9 -9
- package/repo-agent-governance/skills/cabloy-spec-generation/references/canonical-spec-input.md +2 -0
- package/repo-agent-governance/skills/cabloy-spec-generation/references/repo-specs-document-set.md +1 -1
- package/repo-agent-governance/skills/cabloy-spec-generation/references/traceability-and-status-rules.md +1 -1
- package/repo-agent-governance/tests/spec-audit.test.mjs +170 -0
- package/repo-agent-governance/tools/spec-audit/audit.mjs +60 -2
- package/repo-docs/assets/img/cabloy-admin-ssr-cover-en.png +0 -0
- package/repo-docs/index.md +6 -0
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.201
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.201
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Support scoped spec traceability exceptions.
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- Fix the CabloyJS Admin SSR video.
|
|
12
|
+
|
|
13
|
+
### Improvements
|
|
14
|
+
|
|
15
|
+
- Add the CabloyJS Admin SSR video to the demonstrations.
|
|
16
|
+
- Refine the spec parser.
|
|
17
|
+
|
|
3
18
|
## 5.1.200
|
|
4
19
|
|
|
5
20
|
### Features
|
package/README.md
CHANGED
|
@@ -36,6 +36,12 @@ For Cabloy Start, clone the [Cabloy Start repository](https://github.com/cabloy/
|
|
|
36
36
|
|
|
37
37
|
See the [AI Spec-Driven Development](https://cabloy.com/ai/ai-spec-driven-development).
|
|
38
38
|
|
|
39
|
+
## Demonstrations(Videos)
|
|
40
|
+
|
|
41
|
+
### 1. Can an Admin Site Use SSR? CabloyJS in Three Practical Demos (Duration: 1:16)
|
|
42
|
+
|
|
43
|
+
[](https://youtu.be/786IQhRdr1I)
|
|
44
|
+
|
|
39
45
|
## Editions
|
|
40
46
|
|
|
41
47
|
Cabloy is available through two complete project baselines, each maintained in its own repository:
|
package/package.json
CHANGED
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
{
|
|
222
222
|
"adapter": "codex",
|
|
223
223
|
"category": "skill",
|
|
224
|
-
"sha256": "
|
|
224
|
+
"sha256": "df49ea0a8485de0ea6cf345199481836924eed0b68608abf4e6ed829f620b72e",
|
|
225
225
|
"source": "skills/cabloy-spec-generation/references/canonical-spec-input.md",
|
|
226
226
|
"target": ".agents/skills/cabloy-spec-generation/references/canonical-spec-input.md"
|
|
227
227
|
},
|
|
@@ -235,14 +235,14 @@
|
|
|
235
235
|
{
|
|
236
236
|
"adapter": "codex",
|
|
237
237
|
"category": "skill",
|
|
238
|
-
"sha256": "
|
|
238
|
+
"sha256": "95fb6ad6f1f85373a6565442e4f0f50582acafd2bef5c8f1f0056123b0996bc8",
|
|
239
239
|
"source": "skills/cabloy-spec-generation/references/repo-specs-document-set.md",
|
|
240
240
|
"target": ".agents/skills/cabloy-spec-generation/references/repo-specs-document-set.md"
|
|
241
241
|
},
|
|
242
242
|
{
|
|
243
243
|
"adapter": "codex",
|
|
244
244
|
"category": "skill",
|
|
245
|
-
"sha256": "
|
|
245
|
+
"sha256": "45b83917c079306fb56e0c2cf406a894f7c18c9fd8732f97f9902ed02cf32594",
|
|
246
246
|
"source": "skills/cabloy-spec-generation/references/traceability-and-status-rules.md",
|
|
247
247
|
"target": ".agents/skills/cabloy-spec-generation/references/traceability-and-status-rules.md"
|
|
248
248
|
},
|
|
@@ -557,7 +557,7 @@
|
|
|
557
557
|
{
|
|
558
558
|
"adapter": "claude",
|
|
559
559
|
"category": "skill",
|
|
560
|
-
"sha256": "
|
|
560
|
+
"sha256": "df49ea0a8485de0ea6cf345199481836924eed0b68608abf4e6ed829f620b72e",
|
|
561
561
|
"source": "skills/cabloy-spec-generation/references/canonical-spec-input.md",
|
|
562
562
|
"target": ".claude/skills/cabloy-spec-generation/references/canonical-spec-input.md"
|
|
563
563
|
},
|
|
@@ -571,14 +571,14 @@
|
|
|
571
571
|
{
|
|
572
572
|
"adapter": "claude",
|
|
573
573
|
"category": "skill",
|
|
574
|
-
"sha256": "
|
|
574
|
+
"sha256": "95fb6ad6f1f85373a6565442e4f0f50582acafd2bef5c8f1f0056123b0996bc8",
|
|
575
575
|
"source": "skills/cabloy-spec-generation/references/repo-specs-document-set.md",
|
|
576
576
|
"target": ".claude/skills/cabloy-spec-generation/references/repo-specs-document-set.md"
|
|
577
577
|
},
|
|
578
578
|
{
|
|
579
579
|
"adapter": "claude",
|
|
580
580
|
"category": "skill",
|
|
581
|
-
"sha256": "
|
|
581
|
+
"sha256": "45b83917c079306fb56e0c2cf406a894f7c18c9fd8732f97f9902ed02cf32594",
|
|
582
582
|
"source": "skills/cabloy-spec-generation/references/traceability-and-status-rules.md",
|
|
583
583
|
"target": ".claude/skills/cabloy-spec-generation/references/traceability-and-status-rules.md"
|
|
584
584
|
},
|
|
@@ -907,7 +907,7 @@
|
|
|
907
907
|
{
|
|
908
908
|
"adapter": "cursor",
|
|
909
909
|
"category": "skill",
|
|
910
|
-
"sha256": "
|
|
910
|
+
"sha256": "df49ea0a8485de0ea6cf345199481836924eed0b68608abf4e6ed829f620b72e",
|
|
911
911
|
"source": "skills/cabloy-spec-generation/references/canonical-spec-input.md",
|
|
912
912
|
"target": ".cursor/skills/cabloy-spec-generation/references/canonical-spec-input.md"
|
|
913
913
|
},
|
|
@@ -921,14 +921,14 @@
|
|
|
921
921
|
{
|
|
922
922
|
"adapter": "cursor",
|
|
923
923
|
"category": "skill",
|
|
924
|
-
"sha256": "
|
|
924
|
+
"sha256": "95fb6ad6f1f85373a6565442e4f0f50582acafd2bef5c8f1f0056123b0996bc8",
|
|
925
925
|
"source": "skills/cabloy-spec-generation/references/repo-specs-document-set.md",
|
|
926
926
|
"target": ".cursor/skills/cabloy-spec-generation/references/repo-specs-document-set.md"
|
|
927
927
|
},
|
|
928
928
|
{
|
|
929
929
|
"adapter": "cursor",
|
|
930
930
|
"category": "skill",
|
|
931
|
-
"sha256": "
|
|
931
|
+
"sha256": "45b83917c079306fb56e0c2cf406a894f7c18c9fd8732f97f9902ed02cf32594",
|
|
932
932
|
"source": "skills/cabloy-spec-generation/references/traceability-and-status-rules.md",
|
|
933
933
|
"target": ".cursor/skills/cabloy-spec-generation/references/traceability-and-status-rules.md"
|
|
934
934
|
},
|
package/repo-agent-governance/skills/cabloy-spec-generation/references/canonical-spec-input.md
CHANGED
|
@@ -17,6 +17,8 @@ References in matrices, related-record lists, progress, evidence, templates, wil
|
|
|
17
17
|
|
|
18
18
|
Use exact instantiated IDs in declaration-body Traceability. A wildcard or abbreviated range is an aggregate summary, not an exact association or substitute for missing definitions. Keep associations explicit even if a downstream summary matrix repeats them. The audit must derive associations from the declaration body, not nearby unrelated sections.
|
|
19
19
|
|
|
20
|
+
For a genuinely cross-cutting engineering contract without a product requirement, an atomic SRS bullet may carry `Traceability exception: technical-only — <record-specific rationale>` on its definition line. This exempts only the incoming PRD association; its outgoing WBS mapping remains required. For a documentary planning or release gate with no independent executable scenario, a WBS task may carry `Traceability exception: authority-only — <record-specific rationale>` in its declaration body. This exempts only the outgoing ATP association; its incoming SRS mapping remains required. Use exactly one well-formed field on the correct definition and explain the alternative authority or retained documentary checks. The auditor rejects malformed, empty, duplicated, or wrong-kind exceptions. Neither classification waives the underlying contract, delivery checks, applicable ATPs, revision-scoped evidence, or release approval; never use one merely to silence a missing real link.
|
|
21
|
+
|
|
20
22
|
## Minimal connected example
|
|
21
23
|
|
|
22
24
|
These are neutral **syntax examples**, not business requirements to copy into a real suite. All four IDs are exact and unique. Production bodies must describe the approved domain.
|
package/repo-agent-governance/skills/cabloy-spec-generation/references/repo-specs-document-set.md
CHANGED
|
@@ -137,7 +137,7 @@ Every WBS entry should state:
|
|
|
137
137
|
- primary source areas or ownership boundaries;
|
|
138
138
|
- bounded tasks;
|
|
139
139
|
- acceptance/completion checks;
|
|
140
|
-
- linked PRD, SRS, and ATP identifiers;
|
|
140
|
+
- linked PRD, SRS, and ATP identifiers where a real adjacent-authority association exists; a genuine documentary planning/release gate without an independent ATP uses the explicit `authority-only` classification and retains its SRS association and review evidence as specified in `canonical-spec-input.md`;
|
|
141
141
|
- whether it is planned, implemented, or awaiting evidence.
|
|
142
142
|
|
|
143
143
|
Begin with a documentation/decision implementation gate before feature work. For differing Web/Admin strategies, split shared-site integration and independent-site delivery into separate frontend tasks when their source facts, dependencies, or proof differ. An unresolved strategy or exact runtime identifier may block only the affected implementation task; preserve runnable discovery work and unaffected backend or audience work as accurately actionable. Prefer vertical, verifiable increments. Include migration and release hardening as explicit work. Keep `implementation-complete` distinct from `verified`.
|
|
@@ -47,7 +47,7 @@ Minimum cardinality for a material in-scope requirement:
|
|
|
47
47
|
- at least one ATP scenario;
|
|
48
48
|
- zero evidence records before execution, and at least one retained evidence record before `verified`.
|
|
49
49
|
|
|
50
|
-
A single ATP may prove several related requirements, but the matrices must make the relationship explicit. A WBS item may cover several contracts, but it still needs bounded completion checks.
|
|
50
|
+
A single ATP may prove several related requirements, but the matrices must make the relationship explicit. A WBS item may cover several contracts, but it still needs bounded completion checks. The full-chain minimum applies to material in-scope product requirements; a separately sourced engineering obligation or a documentary coordination gate need not invent a product parent or an executable ATP. Use the narrowly scoped declaration-local `Traceability exception` classifications in `canonical-spec-input.md` only for those genuine cases. The unaffected direction still needs an exact-ID link, and review/closure still needs applicable retained proof. An exception is not a deferred status, evidence waiver, acceptance result, or release approval.
|
|
51
51
|
|
|
52
52
|
## Authority-first updates
|
|
53
53
|
|
|
@@ -99,6 +99,176 @@ test('ordinary co-occurrence and wildcard matrices cannot manufacture traceabili
|
|
|
99
99
|
);
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
+
test('technical-only SRS exempts its PRD parent, not its WBS successor', async t => {
|
|
103
|
+
const records = {
|
|
104
|
+
...BASELINE,
|
|
105
|
+
'srs.md':
|
|
106
|
+
'# Contracts\n\n- **SRS-DEMO-01**: Test hygiene. Traceability exception: technical-only — Repository fixture policy owns cleanup.\n\nTraceability: `WBS-DEMO-10-01`\n\n- **SRS-DEMO-02**: Product contract. Traceability: `PRD-DEMO-01`, `WBS-DEMO-10-01`.\n',
|
|
107
|
+
'prd.md': BASELINE['prd.md'].replace('`SRS-DEMO-01`', '`SRS-DEMO-02`'),
|
|
108
|
+
};
|
|
109
|
+
const result = await auditPlanning(await fixture(t, records));
|
|
110
|
+
assert.deepEqual(result.diagnostics, []);
|
|
111
|
+
const withoutException = {
|
|
112
|
+
...records,
|
|
113
|
+
'srs.md': records['srs.md'].replace(
|
|
114
|
+
' Traceability exception: technical-only — Repository fixture policy owns cleanup.',
|
|
115
|
+
'',
|
|
116
|
+
),
|
|
117
|
+
};
|
|
118
|
+
const missingParent = await auditPlanning(await fixture(t, withoutException));
|
|
119
|
+
assert.ok(
|
|
120
|
+
missingParent.diagnostics.some(
|
|
121
|
+
diagnostic => diagnostic.id === 'SRS-DEMO-01' && diagnostic.code === 'traceability-incoming',
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
const missingSuccessor = await auditPlanning(
|
|
125
|
+
await fixture(t, {
|
|
126
|
+
...records,
|
|
127
|
+
'srs.md': records['srs.md'].replace('Traceability: `WBS-DEMO-10-01`\n', ''),
|
|
128
|
+
'pdp-wbs.md': records['pdp-wbs.md'].replace('`SRS-DEMO-01`, ', ''),
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
131
|
+
assert.ok(
|
|
132
|
+
missingSuccessor.diagnostics.some(
|
|
133
|
+
diagnostic => diagnostic.id === 'SRS-DEMO-01' && diagnostic.code === 'traceability-outgoing',
|
|
134
|
+
),
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('authority-only WBS exempts its ATP successor, not its SRS parent', async t => {
|
|
139
|
+
const records = {
|
|
140
|
+
...BASELINE,
|
|
141
|
+
'pdp-wbs.md': `${BASELINE['pdp-wbs.md']}\n#### WBS-DEMO-10-02: Review baseline\n\nTraceability exception: authority-only — Retain the baseline review disposition.\n\nTraceability: \`SRS-DEMO-01\`.\n`,
|
|
142
|
+
'progress.md': `${BASELINE['progress.md']}| WBS-DEMO-10-02 | Review | not-started |\n`,
|
|
143
|
+
};
|
|
144
|
+
const result = await auditPlanning(await fixture(t, records));
|
|
145
|
+
assert.deepEqual(result.diagnostics, []);
|
|
146
|
+
const withoutException = {
|
|
147
|
+
...records,
|
|
148
|
+
'pdp-wbs.md': records['pdp-wbs.md'].replace(
|
|
149
|
+
'Traceability exception: authority-only — Retain the baseline review disposition.\n\n',
|
|
150
|
+
'',
|
|
151
|
+
),
|
|
152
|
+
};
|
|
153
|
+
const missingSuccessor = await auditPlanning(await fixture(t, withoutException));
|
|
154
|
+
assert.ok(
|
|
155
|
+
missingSuccessor.diagnostics.some(
|
|
156
|
+
diagnostic =>
|
|
157
|
+
diagnostic.id === 'WBS-DEMO-10-02' && diagnostic.code === 'traceability-outgoing',
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
const missingParent = await auditPlanning(
|
|
161
|
+
await fixture(t, {
|
|
162
|
+
...records,
|
|
163
|
+
'pdp-wbs.md': records['pdp-wbs.md'].replace('Traceability: `SRS-DEMO-01`.\n', ''),
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
assert.ok(
|
|
167
|
+
missingParent.diagnostics.some(
|
|
168
|
+
diagnostic =>
|
|
169
|
+
diagnostic.id === 'WBS-DEMO-10-02' && diagnostic.code === 'traceability-incoming',
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('malformed, repeated and wrong-kind exceptions fail closed', async t => {
|
|
175
|
+
const invalid = [
|
|
176
|
+
'Traceability exception: technical-only —',
|
|
177
|
+
'Traceability exception: technical-only — !',
|
|
178
|
+
'Traceability exception: unknown — Rationale.',
|
|
179
|
+
'Traceability exception: authority-only — Rationale.',
|
|
180
|
+
'Traceability exception: technical-only — Rationale. Traceability exception: technical-only — Again.',
|
|
181
|
+
];
|
|
182
|
+
for (const field of invalid) {
|
|
183
|
+
const records = {
|
|
184
|
+
...BASELINE,
|
|
185
|
+
'srs.md': BASELINE['srs.md'].replace(
|
|
186
|
+
'### SRS-DEMO-01: Contract\n',
|
|
187
|
+
`- **SRS-DEMO-01**: Contract. ${field}\n`,
|
|
188
|
+
),
|
|
189
|
+
};
|
|
190
|
+
const result = await auditPlanning(await fixture(t, records));
|
|
191
|
+
assert.ok(
|
|
192
|
+
result.diagnostics.some(diagnostic => diagnostic.code === 'traceability-exception'),
|
|
193
|
+
field,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const wrongWbs = {
|
|
197
|
+
...BASELINE,
|
|
198
|
+
'pdp-wbs.md': BASELINE['pdp-wbs.md'].replace(
|
|
199
|
+
'Completion checks:',
|
|
200
|
+
'Traceability exception: technical-only — Not an SRS.\n\nCompletion checks:',
|
|
201
|
+
),
|
|
202
|
+
};
|
|
203
|
+
assert.ok(
|
|
204
|
+
codes(await auditPlanning(await fixture(t, wrongWbs))).includes('traceability-exception'),
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('an exception outside its own declaration cannot exempt a missing link', async t => {
|
|
209
|
+
const records = {
|
|
210
|
+
...BASELINE,
|
|
211
|
+
'srs.md':
|
|
212
|
+
'# Contracts\n\nTraceability exception: technical-only — Preamble.\n\n- **SRS-DEMO-02**: Unmapped contract.\n- **SRS-DEMO-01**: Contract. Traceability exception: technical-only — Different record.\n\nTraceability: `PRD-DEMO-01`, `WBS-DEMO-10-01`\n',
|
|
213
|
+
'pdp-wbs.md': `${BASELINE['pdp-wbs.md'].replace(
|
|
214
|
+
'Dependencies: none.',
|
|
215
|
+
'Dependencies: none.\n\nTraceability exception: authority-only — Phase preamble.',
|
|
216
|
+
)}\n#### WBS-DEMO-10-02: Unmapped task\n`,
|
|
217
|
+
'progress.md': `${BASELINE['progress.md']}| WBS-DEMO-10-02 | Unmapped | not-started |\n`,
|
|
218
|
+
};
|
|
219
|
+
const result = await auditPlanning(await fixture(t, records));
|
|
220
|
+
for (const [id, code] of [
|
|
221
|
+
['SRS-DEMO-02', 'traceability-incoming'],
|
|
222
|
+
['WBS-DEMO-10-02', 'traceability-outgoing'],
|
|
223
|
+
]) {
|
|
224
|
+
assert.ok(
|
|
225
|
+
result.diagnostics.some(diagnostic => diagnostic.id === id && diagnostic.code === code),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
assert.ok(result.diagnostics.some(diagnostic => diagnostic.code === 'traceability-exception'));
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('classified gates retain reference and completed-prerequisite checks', async t => {
|
|
232
|
+
const records = {
|
|
233
|
+
...BASELINE,
|
|
234
|
+
'pdp-wbs.md': `${BASELINE['pdp-wbs.md']}\n#### WBS-DEMO-10-02: Review gate\n\nDependencies: WBS-DEMO-10-01.\n\nTraceability exception: authority-only — Retain the review disposition.\n\nTraceability: \`SRS-DEMO-01\`.\n\nReview \`SRS-MISSING-01\`.\n`,
|
|
235
|
+
'progress.md': `${BASELINE['progress.md']}| WBS-DEMO-10-02 | Review | verified |\n`,
|
|
236
|
+
};
|
|
237
|
+
const result = await auditPlanning(await fixture(t, records));
|
|
238
|
+
assert.ok(
|
|
239
|
+
result.diagnostics.some(
|
|
240
|
+
diagnostic => diagnostic.code === 'undefined-reference' && diagnostic.id === 'SRS-MISSING-01',
|
|
241
|
+
),
|
|
242
|
+
);
|
|
243
|
+
assert.ok(
|
|
244
|
+
result.diagnostics.some(
|
|
245
|
+
diagnostic => diagnostic.code === 'dependency-state' && diagnostic.id === 'WBS-DEMO-10-02',
|
|
246
|
+
),
|
|
247
|
+
);
|
|
248
|
+
assert.ok(
|
|
249
|
+
!result.diagnostics.some(
|
|
250
|
+
diagnostic =>
|
|
251
|
+
diagnostic.code === 'traceability-outgoing' && diagnostic.id === 'WBS-DEMO-10-02',
|
|
252
|
+
),
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('fences and comments cannot annotate a definition', async t => {
|
|
257
|
+
const records = {
|
|
258
|
+
...BASELINE,
|
|
259
|
+
'pdp-wbs.md': `${BASELINE['pdp-wbs.md']}\n#### WBS-DEMO-10-02: Review gate\n\nTraceability: \`SRS-DEMO-01\`.\n\n\`\`\`md\nTraceability exception: authority-only — Example only.\n\`\`\`\n\n<!-- Traceability exception: authority-only — Not authority. -->\n`,
|
|
260
|
+
'progress.md': `${BASELINE['progress.md']}| WBS-DEMO-10-02 | Review | not-started |\n`,
|
|
261
|
+
};
|
|
262
|
+
const result = await auditPlanning(await fixture(t, records));
|
|
263
|
+
assert.ok(
|
|
264
|
+
result.diagnostics.some(
|
|
265
|
+
diagnostic =>
|
|
266
|
+
diagnostic.code === 'traceability-outgoing' && diagnostic.id === 'WBS-DEMO-10-02',
|
|
267
|
+
),
|
|
268
|
+
);
|
|
269
|
+
assert.ok(!codes(result).includes('traceability-exception'));
|
|
270
|
+
});
|
|
271
|
+
|
|
102
272
|
test('lightweight records do not require absent baseline files or charts', async t => {
|
|
103
273
|
const directory = await fixture(t, {
|
|
104
274
|
'README.md': '# Lightweight\n',
|
|
@@ -71,6 +71,46 @@ function addMapping(groups, edges, definitions) {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function traceabilityException(definition, report, annotatedLines) {
|
|
75
|
+
const fields = markdownLines(definition.body ?? '').flatMap(({ text, line }) =>
|
|
76
|
+
Array.from(text.matchAll(/\bTraceability\s+exception\b/gi), match => ({
|
|
77
|
+
text,
|
|
78
|
+
line,
|
|
79
|
+
start: match.index,
|
|
80
|
+
})),
|
|
81
|
+
);
|
|
82
|
+
if (!fields.length) return null;
|
|
83
|
+
for (const field of fields) {
|
|
84
|
+
const line = definition.line + field.line - (definition.kind === 'WBS' ? 0 : 1);
|
|
85
|
+
annotatedLines.add(`${definition.file}:${line}`);
|
|
86
|
+
}
|
|
87
|
+
const field = fields[0];
|
|
88
|
+
const value =
|
|
89
|
+
definition.kind === 'bullet' && field.line === 1
|
|
90
|
+
? field.text.slice(field.start).trim()
|
|
91
|
+
: field.text.trim();
|
|
92
|
+
const match = value.match(/^Traceability exception:\s*(technical-only|authority-only)\s+—\s+/i);
|
|
93
|
+
const exception = match?.[1].toLowerCase();
|
|
94
|
+
const rationale = match && value.slice(match[0].length);
|
|
95
|
+
const validOwner =
|
|
96
|
+
(exception === 'technical-only' &&
|
|
97
|
+
definition.id.startsWith('SRS-') &&
|
|
98
|
+
definition.kind === 'bullet' &&
|
|
99
|
+
field.line === 1) ||
|
|
100
|
+
(exception === 'authority-only' && definition.id.startsWith('WBS-'));
|
|
101
|
+
if (fields.length !== 1 || !validOwner || !/[\p{L}\p{N}]/u.test(rationale ?? '')) {
|
|
102
|
+
report(
|
|
103
|
+
'traceability-exception',
|
|
104
|
+
definition.file,
|
|
105
|
+
definition.line + field.line - (definition.kind === 'WBS' ? 0 : 1),
|
|
106
|
+
'Invalid Traceability exception: use one declaration-local technical-only SRS or authority-only WBS field with a rationale.',
|
|
107
|
+
definition.id,
|
|
108
|
+
);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return exception;
|
|
112
|
+
}
|
|
113
|
+
|
|
74
114
|
function collectMappings(records, definitions) {
|
|
75
115
|
const edges = new Map(Array.from(definitions.keys(), id => [id, new Set()]));
|
|
76
116
|
for (const definition of definitions.values()) {
|
|
@@ -271,8 +311,22 @@ export async function auditPlanning(
|
|
|
271
311
|
report('declaration', file, 1, error.message);
|
|
272
312
|
}
|
|
273
313
|
}
|
|
314
|
+
const exceptions = new Map();
|
|
315
|
+
const annotatedLines = new Set();
|
|
316
|
+
for (const definition of definitions.values()) {
|
|
317
|
+
const exception = traceabilityException(definition, report, annotatedLines);
|
|
318
|
+
if (exception) exceptions.set(definition.id, exception);
|
|
319
|
+
}
|
|
274
320
|
for (const [file, markdown] of records) {
|
|
275
321
|
for (const { text, line } of markdownLines(markdown)) {
|
|
322
|
+
if (/\bTraceability\s+exception\b/i.test(text) && !annotatedLines.has(`${file}:${line}`)) {
|
|
323
|
+
report(
|
|
324
|
+
'traceability-exception',
|
|
325
|
+
file,
|
|
326
|
+
line,
|
|
327
|
+
'Traceability exception must be in its own formal declaration.',
|
|
328
|
+
);
|
|
329
|
+
}
|
|
276
330
|
for (const prefix of OWNERS.keys()) {
|
|
277
331
|
for (const id of findIdentifiers(referenceText(text), prefix)) {
|
|
278
332
|
if (!definitions.has(id)) {
|
|
@@ -347,7 +401,7 @@ export async function auditPlanning(
|
|
|
347
401
|
const incoming = new Set([...edges.values()].flatMap(values => [...values]));
|
|
348
402
|
for (const [id, definition] of definitions) {
|
|
349
403
|
if (definition.status === 'deferred' || definition.deferred) continue;
|
|
350
|
-
if (!id.startsWith('PRD-') && !incoming.has(id)) {
|
|
404
|
+
if (!id.startsWith('PRD-') && !incoming.has(id) && exceptions.get(id) !== 'technical-only') {
|
|
351
405
|
report(
|
|
352
406
|
'traceability-incoming',
|
|
353
407
|
definition.file,
|
|
@@ -356,7 +410,11 @@ export async function auditPlanning(
|
|
|
356
410
|
id,
|
|
357
411
|
);
|
|
358
412
|
}
|
|
359
|
-
if (
|
|
413
|
+
if (
|
|
414
|
+
!id.startsWith('ATP-') &&
|
|
415
|
+
!edges.get(id).size &&
|
|
416
|
+
exceptions.get(id) !== 'authority-only'
|
|
417
|
+
) {
|
|
360
418
|
report(
|
|
361
419
|
'traceability-outgoing',
|
|
362
420
|
definition.file,
|
|
Binary file
|
package/repo-docs/index.md
CHANGED
|
@@ -30,6 +30,12 @@ features:
|
|
|
30
30
|
details: Organize capabilities for SSR, SPA, Web, and Admin applications with shared conventions.
|
|
31
31
|
---
|
|
32
32
|
|
|
33
|
+
## Demonstrations(Videos)
|
|
34
|
+
|
|
35
|
+
### 1. Can an Admin Site Use SSR? CabloyJS in Three Practical Demos (Duration: 1:16)
|
|
36
|
+
|
|
37
|
+
[](https://youtu.be/786IQhRdr1I)
|
|
38
|
+
|
|
33
39
|
## Choose a reading path
|
|
34
40
|
|
|
35
41
|
### Start a project
|