@sdd-method/author-cli 0.12.0 → 0.12.1

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.
@@ -14,7 +14,10 @@
14
14
  // ## Trigger
15
15
  // ...
16
16
  //
17
- // Profile gating: journeys are an application-product profile concept.
17
+ // Any SDD catalogues the journeys it owns: an application its own, and a platform root a journey that no single
18
+ // application owns (a cross-application journey, M-ADR 0146, or one that runs through platform services only), whose
19
+ // journey feature names it with @journey:<id> (M-ADR 0229 §4.2, option B). **App:** is omitted where no app owns it.
20
+ // Only an application is expected to have journeys/, so only there is its absence a coverage finding.
18
21
  // id = `{product_id}-{slug}`, slug from filename stem.
19
22
  import { readdir, readFile } from "node:fs/promises";
20
23
  import { basename, join, relative } from "node:path";
@@ -26,17 +29,6 @@ export async function buildUserJourneys(inputs) {
26
29
  const relatedFeatureSlugsById = new Map();
27
30
  const primaryPersonaById = new Map();
28
31
  const supportingPersonasById = new Map();
29
- if (inputs.profile !== "application-product") {
30
- return {
31
- rows: [],
32
- idByPath,
33
- idBySlug,
34
- featurePathsById,
35
- relatedFeatureSlugsById,
36
- primaryPersonaById,
37
- supportingPersonasById,
38
- };
39
- }
40
32
  const dir = join(inputs.sddPath, JOURNEYS_DIR_REL);
41
33
  let entries;
42
34
  try {
@@ -45,7 +37,10 @@ export async function buildUserJourneys(inputs) {
45
37
  catch (err) {
46
38
  if (err.code !== "ENOENT")
47
39
  throw err;
48
- inputs.findings.add("coverage", `${JOURNEYS_DIR_REL}/ not found — emitting empty user_journeys.csv`);
40
+ // An application is expected to describe its journeys; another SDD holds journeys only where it owns one.
41
+ if (inputs.profile === "application-product") {
42
+ inputs.findings.add("coverage", `${JOURNEYS_DIR_REL}/ not found — emitting empty user_journeys.csv`);
43
+ }
49
44
  return {
50
45
  rows: [],
51
46
  idByPath,
@@ -9,7 +9,7 @@
9
9
  // never the proposal's.
10
10
  // unnamed a listed journey the proposal's `## Affected Journeys` section does not name.
11
11
  // unshown a journey named `altered` with nothing to show it: no delta in the change to a feature of kind journey
12
- // whose id is the journey's id or ends with it (option B), and no task naming it (option A).
12
+ // whose delta or living spec tags it @journey:<journey-id> (option B), and no task naming it (option A).
13
13
  // A proposal line is `- <journey-id>: altered ...` or `- <journey-id>: unaffected, <reason>`; an unaffected line
14
14
  // with no reason is reported too.
15
15
  //
@@ -52,6 +52,10 @@ async function csv(repo, name, dir = "catalogues") {
52
52
  return text === null ? null : parseCatalogueCsv(text).rows;
53
53
  }
54
54
  /** The `## Affected Journeys` section: journey id -> what the proposal says of it. */
55
+ /** The journey ids a spec tags with @journey:<id>. */
56
+ export function journeyTags(text) {
57
+ return new Set([...text.matchAll(/@journey:([a-z0-9][a-z0-9-]*)/g)].map((m) => m[1]));
58
+ }
55
59
  export function parseAffectedJourneys(proposal) {
56
60
  const out = new Map();
57
61
  let inSection = false;
@@ -178,7 +182,18 @@ export async function checkJourneyImpact(repo) {
178
182
  if (n.verdict === "unaffected" && n.rest === "")
179
183
  warnings.push(`${CHANGES_DIR}/${change}/proposal.md: journey ${j} is named unaffected without a reason`);
180
184
  if (n.verdict === "altered") {
181
- const journeyDelta = [...deltaFeatures].some((f) => featureKinds.get(f) === "journey" && (f === j || f.endsWith(`-${j}`)));
185
+ // Option B: a delta to a feature of kind journey that names this journey with its @journey tag, in the delta
186
+ // or in the living spec it changes (M-ADR 0229 §4.2). The tag, not the feature's id, binds the two.
187
+ let journeyDelta = false;
188
+ for (const f of deltaFeatures) {
189
+ if (featureKinds.get(f) !== "journey")
190
+ continue;
191
+ const text = `${(await readText(join(dir, "specs", f, "spec.md"))) ?? ""}\n${(await readText(join(repo, "openspec", "specs", f, "spec.md"))) ?? ""}`;
192
+ if (journeyTags(text).has(j)) {
193
+ journeyDelta = true;
194
+ break;
195
+ }
196
+ }
182
197
  const shown = journeyDelta || tasks.includes(j);
183
198
  if (!shown)
184
199
  warnings.push(`${CHANGES_DIR}/${change}/proposal.md: journey ${j} is named altered, and the change shows nothing for it: no delta to its journey feature and no task naming it`);
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "0.13.0",
3
- "built_against_sdd_cli": "4.0.0"
2
+ "version": "0.13.1",
3
+ "built_against_sdd_cli": "4.0.1"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "4.0.0",
3
- "built_against_sdd_cli": "4.0.0"
2
+ "version": "4.0.1",
3
+ "built_against_sdd_cli": "4.0.1"
4
4
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sdd-method/author-cli",
3
- "version": "0.12.0",
4
- "sddCliVersion": "4.0.0",
3
+ "version": "0.12.1",
4
+ "sddCliVersion": "4.0.1",
5
5
  "description": "Re-author one SDD's content — scaffold, plan, contracts, conformance, api-surface, snapshots, briefings, catalogue check — plus every read-only verify verb. Ships only the code those verbs reach; it cannot sync the baseline or rebuild the catalogues. Maintenance is in @sdd-method/maintain-cli, administration in @sdd-method/sdd-cli.",
6
6
  "license": "UNLICENSED",
7
7
  "type": "module",