@formicoidea/labre-framework-wardley 0.31.0 → 0.32.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 (46) hide show
  1. package/dist/actions.d.ts +14 -18
  2. package/dist/actions.js +79 -38
  3. package/dist/audit-criteria.d.ts +31 -0
  4. package/dist/audit-criteria.js +90 -0
  5. package/dist/background.d.ts +14 -0
  6. package/dist/background.js +338 -0
  7. package/dist/commands.d.ts +5 -0
  8. package/dist/commands.js +190 -0
  9. package/dist/consts.d.ts +10 -4
  10. package/dist/consts.js +10 -9
  11. package/dist/descriptor.d.ts +8 -3
  12. package/dist/descriptor.js +6 -3
  13. package/dist/element-renderer.d.ts +8 -5
  14. package/dist/element-renderer.js +12 -147
  15. package/dist/element-view.d.ts +8 -4
  16. package/dist/element-view.js +30 -22
  17. package/dist/gradient.d.ts +6 -11
  18. package/dist/gradient.js +58 -47
  19. package/dist/index.d.ts +9 -1
  20. package/dist/index.js +9 -1
  21. package/dist/legend.js +8 -0
  22. package/dist/natures.d.ts +50 -0
  23. package/dist/natures.js +93 -0
  24. package/dist/nudges.d.ts +41 -0
  25. package/dist/nudges.js +69 -0
  26. package/dist/profiles.d.ts +2 -0
  27. package/dist/profiles.js +87 -0
  28. package/dist/reading.d.ts +3 -0
  29. package/dist/reading.js +129 -0
  30. package/dist/roles.d.ts +50 -0
  31. package/dist/roles.js +132 -0
  32. package/dist/rules.d.ts +2 -0
  33. package/dist/rules.js +270 -0
  34. package/dist/templates/index.js +51 -7
  35. package/dist/templates/maps.js +121 -17
  36. package/dist/toolbar/wardley-menu.d.ts +8 -15
  37. package/dist/toolbar/wardley-menu.js +8 -136
  38. package/dist/translations.d.ts +16 -0
  39. package/dist/translations.js +24 -0
  40. package/dist/view.d.ts +17 -0
  41. package/dist/view.js +116 -13
  42. package/package.json +2 -2
  43. package/dist/label-layout.d.ts +0 -20
  44. package/dist/label-layout.js +0 -72
  45. package/dist/shortcuts.d.ts +0 -2
  46. package/dist/shortcuts.js +0 -37
package/dist/nudges.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Wardley **map quality** — the checklist (PF13.9).
3
+ *
4
+ * Level 1 is `./rules.ts`: deterministic, decidable, real time. This file is
5
+ * level 2, and the split is the whole point of the taxonomy — a rule only enters
6
+ * the deterministic engine if an algorithm can decide it on persisted data
7
+ * inside the ~16 ms budget. Everything else is a NUDGE: an expectation offered
8
+ * as a checklist, never evaluated, never blocking anything. Ticking is assuming.
9
+ *
10
+ * ## The check-up rules are gone (PO decision, 02/08/2026)
11
+ *
12
+ * Q5 (tone convention) and Q6 (phase nomenclature) used to live beside these,
13
+ * as on-demand rules behind a "Run check-up" button. The panel is now the
14
+ * checklist and nothing else: the PO's recette found that a second verdict, a
15
+ * button and a scope line asked the reader to hold three different kinds of
16
+ * statement in their head over a map that mostly wanted a title. Nothing was
17
+ * deleted from the platform — the on-demand MOMENT (PF5.14) and the
18
+ * `tone-convention` / `majority-fact` families are still in the engine, still
19
+ * tested, and the next framework that wants a check-up declares one. Wardley
20
+ * simply stopped exposing one.
21
+ *
22
+ * Registered from the flag-gated `WardleyViewExtension`, beside the rules and
23
+ * the profiles: a checklist is tooling. Switching the flag off takes it away and
24
+ * leaves the ticks written on the map, unread, until it comes back (PF7.10).
25
+ */
26
+ /**
27
+ * **Q1–Q4** — the four things a Wardley map needs in order to do its job, and
28
+ * that no algorithm can check.
29
+ *
30
+ * Every one of them is about whether the map can be DISCUSSED. A map with no
31
+ * title is a diagram nobody can situate; one with no legend is a private
32
+ * notation; one whose evolution axis is drawn but not used is a value chain
33
+ * wearing a Wardley costume. The tool can put all four on screen and it cannot
34
+ * judge a single one — "the title contextualises the study" is a statement about
35
+ * meaning, and a rule claiming to have verified it would be lying.
36
+ *
37
+ * The wordings are the PO's own, from the review of 01/08/2026, carried as
38
+ * `fallback` so a host with no catalogue reads them exactly as written.
39
+ */
40
+ export const WARDLEY_NUDGES = [
41
+ {
42
+ id: 'wardley.q1-title',
43
+ framework: 'wardley',
44
+ labelKey: 'com.labre.wardley.quality.title',
45
+ fallback: 'The map has a title that frames the study (scope, objective) and states what kind of map it is.',
46
+ order: 1,
47
+ },
48
+ {
49
+ id: 'wardley.q2-context',
50
+ framework: 'wardley',
51
+ labelKey: 'com.labre.wardley.quality.context',
52
+ fallback: 'The context of the map is stated.',
53
+ order: 2,
54
+ },
55
+ {
56
+ id: 'wardley.q3-legend',
57
+ framework: 'wardley',
58
+ labelKey: 'com.labre.wardley.quality.legend',
59
+ fallback: 'The map is legended.',
60
+ order: 3,
61
+ },
62
+ {
63
+ id: 'wardley.q4-evolution-axis',
64
+ framework: 'wardley',
65
+ labelKey: 'com.labre.wardley.quality.evolution-axis',
66
+ fallback: 'The evolution axis is used and legended.',
67
+ order: 4,
68
+ },
69
+ ];
@@ -0,0 +1,2 @@
1
+ import type { ValidationProfile } from '@formicoidea/labre-core/blocks/surface';
2
+ export declare const WARDLEY_PROFILES: readonly ValidationProfile[];
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Wardley validation profiles (PF9).
3
+ *
4
+ * DATA owned by the framework, like its rules, its roles and its background:
5
+ * the engine knows how to APPLY a profile, never which ones Wardley has. Adding
6
+ * a level of requirement is adding an entry to this array.
7
+ *
8
+ * A profile is chosen per MAP, not per document (PF9.1) — a rough sketch and a
9
+ * deliverable diagram coexist on the same canvas with different requirements,
10
+ * and the choice rides on the background element itself.
11
+ *
12
+ * Registered from the flag-gated `WardleyViewExtension`, beside the rules:
13
+ * switching the Wardley flag off takes the choice away with the rest of the
14
+ * tooling, and a map already set to `strict` simply stops being checked until
15
+ * it comes back — the id stays written, untouched.
16
+ */
17
+ /**
18
+ * Learning: every rule is demoted to `audit`, so findings still reach
19
+ * `violations$` — a host panel and a conformance report see them — and the
20
+ * canvas says nothing at all. Nobody drawing gets interrupted.
21
+ *
22
+ * The DEFAULT, and deliberately so: the sketch wins (PRD principle 3). Somebody
23
+ * opening a Wardley map for the first time is exploring, not filing a
24
+ * deliverable, and the tool has no business telling them off for an arrow they
25
+ * have not finished turning round. Being the default also means it is the one
26
+ * profile that WRITES NOTHING: a map on `sketch` carries no profile key, so
27
+ * every map ever drawn is on it, with no migration and no backfill.
28
+ *
29
+ * Silencing ALL THREE is the architect's recommendation and is written here for
30
+ * the PO to confirm or overturn — it is one word per line either way.
31
+ */
32
+ const sketch = {
33
+ id: 'wardley.sketch',
34
+ framework: 'wardley',
35
+ labelKey: 'com.labre.wardley.profile.sketch',
36
+ fallback: 'Sketch',
37
+ isDefault: true,
38
+ rules: {
39
+ 'wardley.change-arrow-against-evolution': 'audit',
40
+ 'wardley.inertia-off-transition': 'audit',
41
+ 'wardley.overlapping-artefacts': 'audit',
42
+ // W4 too, and it is the one where the demotion matters most: a value chain
43
+ // is drawn before it is arranged, so half the links are momentarily
44
+ // upside-down while somebody thinks. The finding still reaches a host panel
45
+ // and a conformance report; the canvas says nothing.
46
+ 'wardley.provider-above-consumer': 'audit',
47
+ },
48
+ };
49
+ /**
50
+ * Strict: the deliverable level. Every rule bites, and every severity a user
51
+ * gets is spelled out here rather than left absent — PF9.4 wants the answer
52
+ * readable in one place, and a profile that says "warning" out loud cannot be
53
+ * misread as "whatever the rule happens to declare this quarter".
54
+ *
55
+ * ## Why W1 and W2 are not `blocking-overridable`
56
+ *
57
+ * They should be: pointing a change arrow backwards and parking an inertia bar
58
+ * in the middle of a phase are both statements that are simply false, and the
59
+ * architect's recommendation is that strict refuse the gesture and offer the
60
+ * way out.
61
+ *
62
+ * Nothing implements that yet. `blocking-overridable` is carried by the engine
63
+ * and read by nobody: no gesture is refused anywhere in this library. Declaring
64
+ * it here would be data claiming an effect that does not exist, and the honest
65
+ * move is a `warning` plus this paragraph. When the refusal lands, this is a
66
+ * two-word change and the profile is the only file that moves.
67
+ *
68
+ * W3 stays `warning` on its own merits: an overlap is a readability problem,
69
+ * and refusing a drag because two labels touch would be the tool fighting the
70
+ * hand.
71
+ */
72
+ const strict = {
73
+ id: 'wardley.strict',
74
+ framework: 'wardley',
75
+ labelKey: 'com.labre.wardley.profile.strict',
76
+ fallback: 'Strict',
77
+ rules: {
78
+ 'wardley.change-arrow-against-evolution': 'warning',
79
+ 'wardley.inertia-off-transition': 'warning',
80
+ 'wardley.overlapping-artefacts': 'warning',
81
+ // W4 is a `warning` for the same reason W1 and W2 are, plus one of its own:
82
+ // its two honest resolutions are a MOVE and a REVERSAL, and both are the
83
+ // user's call. A rule that refused the gesture would be picking one.
84
+ 'wardley.provider-above-consumer': 'warning',
85
+ },
86
+ };
87
+ export const WARDLEY_PROFILES = [sketch, strict];
@@ -0,0 +1,3 @@
1
+ import type { ReadingNamingConvention, ReadingProfile } from '@formicoidea/labre-core/blocks/surface';
2
+ export declare const WARDLEY_NAMING_CONVENTIONS: readonly ReadingNamingConvention[];
3
+ export declare const WARDLEY_READING: ReadingProfile;
@@ -0,0 +1,129 @@
1
+ import { WARDLEY_BACKGROUND } from './background.js';
2
+ import { WARDLEY_NATURE, WARDLEY_NATURE_TAG_ID } from './natures.js';
3
+ import { WARDLEY_ROLE, WARDLEY_ROLES } from './roles.js';
4
+ /**
5
+ * What Wardley lets the tool read of a component (MF3, the reversed reading).
6
+ *
7
+ * DATA, like the rules and the profiles beside it: the engine
8
+ * (`@labre/affine-block-surface`) knows how to read a role, a tag, a typed edge
9
+ * and a declared background — it knows nothing about Wardley. Everything below
10
+ * is already stated somewhere else in this framework and is merely POINTED AT
11
+ * here: the role vocabulary (`roles.ts`), the nature tag (`natures.ts`), the
12
+ * dependency edge (ADR 0010 § 2 tier 2) and the map's own zones
13
+ * (`background.ts`).
14
+ *
15
+ * Registered from the FLAG-GATED view extension: reading a map is tooling, so a
16
+ * board whose Wardley flag is off keeps every element it has and simply stops
17
+ * being read (ADR 0009).
18
+ */
19
+ /**
20
+ * ## The naming convention, and why it is one motif rather than a grammar
21
+ *
22
+ * Wardley's four natures answer "what KIND of thing is this?", and two of the
23
+ * four answers are visible in the NAME long before anyone opens a picker: an
24
+ * activity is something that is DONE, and the other three are things that
25
+ * ARE — something recorded, a way of doing, something known.
26
+ *
27
+ * So the convention retained is exactly one motif — **does the name read as an
28
+ * action?** — expressed as the English gerund (`…ing`), applied POSITIVELY to
29
+ * `activity` and NEGATIVELY to the three others:
30
+ *
31
+ * | nature | expected | example |
32
+ * | --------- | --------------------------------- | -------------------- |
33
+ * | activity | reads as an action | "Brewing tea" |
34
+ * | data | does NOT read as an action | "Customer register" |
35
+ * | practice | does NOT read as an action | "Agile method" |
36
+ * | knowledge | does NOT read as an action | "Thermodynamics" |
37
+ *
38
+ * One motif, four entries, no word list. A vocabulary of "practice-ish nouns"
39
+ * would be a dictionary the library would then own, in one language, and would
40
+ * be wrong for the first client who names things in their own — whereas the
41
+ * gerund is a single, checkable shape a user can predict from the sentence they
42
+ * are shown.
43
+ *
44
+ * ## The motif is ENGLISH, and it says so
45
+ *
46
+ * `\p{L}+ing` is a fact about English, so every entry below declares
47
+ * `lang: 'en'` and the engine keeps quiet unless the host says it is serving
48
+ * English. That is not a detail: on a board named in French the same motif is
49
+ * wrong in BOTH directions — "Facturation" on an activity would be told to use
50
+ * a verb, and "Planning", "Sourcing" or "Reporting" on data would be told they
51
+ * read as actions. An out-of-scope suggestion is worse than none, because it is
52
+ * confident.
53
+ *
54
+ * Extending the coverage is adding entries, not changing code: one more
55
+ * convention per `valueId` with its own `lang` and its own motif (French, say,
56
+ * would key on the `-tion`/`-ment` nominalisations against an infinitive), and
57
+ * the first one in scope wins. A host that declares no language gets silence,
58
+ * which is the honest answer to "I do not know what language these words are
59
+ * in".
60
+ *
61
+ * It is a SUGGESTION and never a verdict: it produces no violation, blocks no
62
+ * gesture, and the panel prints the wording below rather than a red mark. A
63
+ * component named "Tea" whose nature is `activity` is very often correct — the
64
+ * architect writing shorthand — and the reading's job is to ask, not to insist.
65
+ *
66
+ * The wording travels as an i18n key with an English fallback, like every other
67
+ * declaration in this framework: the host localises it, and a host with no
68
+ * catalogue still reads a real sentence.
69
+ */
70
+ const ACTION_MOTIF = String.raw `\b\p{L}+ing\b`;
71
+ const naming = (valueId, expectsAction, key, fallback) => ({
72
+ valueId,
73
+ // The gerund is a fact about English: out of that scope the engine says
74
+ // nothing rather than something confident and wrong.
75
+ lang: 'en',
76
+ // The negative form is the same motif under a lookahead, so the two can never
77
+ // drift apart: change what "reads as an action" means and both sides move.
78
+ // `[\s\S]` rather than `.` because a canvas label wraps, and a name on two
79
+ // lines must be judged on what it says, not on where it broke.
80
+ pattern: expectsAction
81
+ ? ACTION_MOTIF
82
+ : `^(?![\\s\\S]*${ACTION_MOTIF})[\\s\\S]+$`,
83
+ hintKey: key,
84
+ hintFallback: fallback,
85
+ });
86
+ export const WARDLEY_NAMING_CONVENTIONS = [
87
+ naming(WARDLEY_NATURE.activity, true, 'com.labre.wardley.reading.naming.activity', 'An activity is something that is done — name it with a verb ("Brewing tea"), not with a thing.'),
88
+ naming(WARDLEY_NATURE.data, false, 'com.labre.wardley.reading.naming.data', 'Data is something recorded — name it as a thing ("Customer register"), not as an action.'),
89
+ naming(WARDLEY_NATURE.practice, false, 'com.labre.wardley.reading.naming.practice', 'A practice is a way of doing — name the practice ("Agile method"), not the doing.'),
90
+ naming(WARDLEY_NATURE.knowledge, false, 'com.labre.wardley.reading.naming.knowledge', 'Knowledge is something known — name what is known ("Thermodynamics"), not an activity.'),
91
+ ];
92
+ export const WARDLEY_READING = {
93
+ id: 'wardley',
94
+ framework: 'wardley',
95
+ roles: WARDLEY_ROLES,
96
+ // The subject is a component. `market` and `ecosystem` specialise it and are
97
+ // read for free through `roleIsA` — that is the entire reason role hierarchy
98
+ // is data. The `anchor` (a user / need) is deliberately NOT a child of
99
+ // `component` and is deliberately not read: a need has no nature and no
100
+ // phase, it has a demand.
101
+ appliesTo: WARDLEY_ROLE.component,
102
+ // On this canvas a name IS a separate free text element grouped with the
103
+ // node — the same fact W3 measures overlaps against.
104
+ labelRole: WARDLEY_ROLE.label,
105
+ nature: {
106
+ tagId: WARDLEY_NATURE_TAG_ID,
107
+ conventions: WARDLEY_NAMING_CONVENTIONS,
108
+ },
109
+ // The value-chain link. ADR 0010 § 2: `source` is the subject of the role's
110
+ // verb, and this role's verb is "depends on" — so the source is the consumer
111
+ // and the target is what it needs.
112
+ relation: { edgeRole: WARDLEY_ROLE.dependency },
113
+ frame: {
114
+ backgroundRole: WARDLEY_ROLE.map,
115
+ background: WARDLEY_BACKGROUND,
116
+ // Evolution runs along the plot's x axis; its four zones ARE the phases.
117
+ axis: 'x',
118
+ },
119
+ /**
120
+ * The two record properties a reading may compare itself against.
121
+ *
122
+ * Host keys, and plain ones on purpose: the library does not name a property
123
+ * of somebody else's document, it states which two it would read IF the host
124
+ * ships them (and lists them in `hoverFields`). A host whose record spells
125
+ * them differently gets no comparison and no drift — silence, which is the
126
+ * correct answer to "I cannot see the record's nature".
127
+ */
128
+ recordKeys: { nature: 'nature', phase: 'phase' },
129
+ };
@@ -0,0 +1,50 @@
1
+ import type { WardleyNodeKind } from '@formicoidea/labre-core/model';
2
+ import type { RoleDefs } from '@formicoidea/labre-core/std/gfx';
3
+ /**
4
+ * Wardley role vocabulary (PF13.1).
5
+ *
6
+ * Roles are the semantic identity of a Wardley artefact — no rule will ever
7
+ * look at a shape type. They mirror {@link WardleyNodeKind}, which the user
8
+ * already picks when choosing the artefact in the toolbox, plus the typed
9
+ * `dependency` EDGE carried by the Wardley link connector.
10
+ *
11
+ * Hierarchy is DATA (`parent`), never TS inheritance: `market` and `ecosystem`
12
+ * specialise `component`, so a rule written on `wardley:component` applies to
13
+ * them for free (see `roleIsA`). The `anchor` (user / need) is a role of its
14
+ * own — a component with no need of its own — and is deliberately NOT a child
15
+ * of `component`.
16
+ */
17
+ /**
18
+ * ## Revision of decision #71 (PF13.4, 01/08/2026)
19
+ *
20
+ * PF1 left three artefacts NEUTRAL — the evolution arrow ("a movement
21
+ * annotation, not a dependency"), the inertia bar and the node labels — on the
22
+ * ground that nothing was written on them yet. That was right while the only
23
+ * rule was the pilot; it is wrong now that the framework has rules about them:
24
+ * an element with no role is never evaluated (proportionality, PRD principle
25
+ * 8), so leaving these neutral would have meant W1, W2 and W3 could not exist.
26
+ *
27
+ * The reversal writes new VALUES into the existing `role` field. No schema
28
+ * change, no migration, and no backfill of documents already drawn: a map
29
+ * authored before today carries arrows and bars with no role, so it raises
30
+ * nothing — which is the same promise every earlier role made.
31
+ */
32
+ /** Every role this framework declares. */
33
+ export type WardleyRole = WardleyNodeKind | 'dependency' | 'map' | 'change-arrow' | 'inertia' | 'label';
34
+ export type WardleyRoleId = `wardley:${WardleyRole}`;
35
+ /** Role ids, keyed by the `kind` used at the creation sites. */
36
+ export declare const WARDLEY_ROLE: {
37
+ readonly component: "wardley:component";
38
+ readonly anchor: "wardley:anchor";
39
+ readonly pipeline: "wardley:pipeline";
40
+ readonly handle: "wardley:handle";
41
+ readonly market: "wardley:market";
42
+ readonly ecosystem: "wardley:ecosystem";
43
+ readonly method: "wardley:method";
44
+ readonly dependency: "wardley:dependency";
45
+ readonly map: "wardley:map";
46
+ readonly changeArrow: "wardley:change-arrow";
47
+ readonly inertia: "wardley:inertia";
48
+ readonly label: "wardley:label";
49
+ };
50
+ export declare const WARDLEY_ROLES: RoleDefs;
package/dist/roles.js ADDED
@@ -0,0 +1,132 @@
1
+ /** Role ids, keyed by the `kind` used at the creation sites. */
2
+ export const WARDLEY_ROLE = {
3
+ component: 'wardley:component',
4
+ anchor: 'wardley:anchor',
5
+ pipeline: 'wardley:pipeline',
6
+ handle: 'wardley:handle',
7
+ market: 'wardley:market',
8
+ ecosystem: 'wardley:ecosystem',
9
+ method: 'wardley:method',
10
+ dependency: 'wardley:dependency',
11
+ map: 'wardley:map',
12
+ changeArrow: 'wardley:change-arrow',
13
+ inertia: 'wardley:inertia',
14
+ label: 'wardley:label',
15
+ };
16
+ const DEFS = [
17
+ {
18
+ id: WARDLEY_ROLE.component,
19
+ kind: 'node',
20
+ labelKey: 'com.labre.wardley.role.component',
21
+ },
22
+ // A user / need: a component with no need of its own, hence its own role
23
+ // rather than a specialisation of `component`.
24
+ {
25
+ id: WARDLEY_ROLE.anchor,
26
+ kind: 'node',
27
+ labelKey: 'com.labre.wardley.role.anchor',
28
+ },
29
+ // The pipeline body. Connections go through its handle, never the body.
30
+ {
31
+ id: WARDLEY_ROLE.pipeline,
32
+ kind: 'node',
33
+ labelKey: 'com.labre.wardley.role.pipeline',
34
+ },
35
+ {
36
+ id: WARDLEY_ROLE.handle,
37
+ kind: 'node',
38
+ labelKey: 'com.labre.wardley.role.handle',
39
+ },
40
+ {
41
+ id: WARDLEY_ROLE.market,
42
+ parent: WARDLEY_ROLE.component,
43
+ kind: 'node',
44
+ labelKey: 'com.labre.wardley.role.market',
45
+ },
46
+ {
47
+ id: WARDLEY_ROLE.ecosystem,
48
+ parent: WARDLEY_ROLE.component,
49
+ kind: 'node',
50
+ labelKey: 'com.labre.wardley.role.ecosystem',
51
+ },
52
+ {
53
+ id: WARDLEY_ROLE.method,
54
+ kind: 'node',
55
+ labelKey: 'com.labre.wardley.role.method',
56
+ },
57
+ /**
58
+ * The value-chain link: "A depends on B".
59
+ *
60
+ * The comment above was the whole of the convention until `docs/adr/0010`
61
+ * made it DATA. Tier 1 of that ADR is generic — `source` is the subject of
62
+ * the role's verb, `target` its object — and tier 2 is this `direction`
63
+ * block: the verb of THIS role is "depends on", so its source is the
64
+ * consumer and its target is what the consumer needs. Needs descend from
65
+ * source to target; value flows back up. W4 evaluates exactly that reading,
66
+ * and the tool hint below is what makes it a statement the user made rather
67
+ * than a by-product of which end their finger landed on first.
68
+ */
69
+ {
70
+ id: WARDLEY_ROLE.dependency,
71
+ kind: 'edge',
72
+ labelKey: 'com.labre.wardley.role.dependency',
73
+ labelFallback: 'Dependency',
74
+ direction: {
75
+ verbKey: 'com.labre.wardley.role.dependency.verb',
76
+ verbFallback: 'depends on',
77
+ gestureHintKey: 'com.labre.wardley.role.dependency.gesture',
78
+ gestureHintFallback: 'Drag from the component that has the need to what it needs.',
79
+ },
80
+ },
81
+ // The map itself: the axes frame the other roles are positioned against.
82
+ // A frame, not a component, so it specialises nothing — a rule written on
83
+ // `wardley:component` must never match the map it measures against.
84
+ {
85
+ id: WARDLEY_ROLE.map,
86
+ kind: 'node',
87
+ labelKey: 'com.labre.wardley.role.map',
88
+ },
89
+ // The change (evolution) arrow: "this is where it is going". An EDGE, and
90
+ // deliberately not a specialisation of `dependency` — it says nothing about
91
+ // what depends on what, and W1 must not fall on a value-chain link.
92
+ {
93
+ id: WARDLEY_ROLE.changeArrow,
94
+ kind: 'edge',
95
+ labelKey: 'com.labre.wardley.role.change-arrow',
96
+ labelFallback: 'Evolution arrow',
97
+ // Tier 1 applies to every edge role, so this one states its own verb — and
98
+ // it is NOT "depends on". An arrow says where a component is going: source
99
+ // is where it stands today, target where it is heading. W4 reads the
100
+ // dependency's verb and never this one.
101
+ direction: {
102
+ verbKey: 'com.labre.wardley.role.change-arrow.verb',
103
+ verbFallback: 'is evolving towards',
104
+ gestureHintKey: 'com.labre.wardley.role.change-arrow.gesture',
105
+ gestureHintFallback: 'Drag from where the component stands today to where it is heading.',
106
+ },
107
+ },
108
+ // The inertia bar: resistance to a movement, drawn ASTRIDE the phase divider
109
+ // the thing refuses to cross (W2). A plain filled rect on the canvas, which
110
+ // is exactly why it needs a role — nothing about its shape says what it
111
+ // means.
112
+ {
113
+ id: WARDLEY_ROLE.inertia,
114
+ kind: 'node',
115
+ labelKey: 'com.labre.wardley.role.inertia',
116
+ },
117
+ // The name written next to an artefact. A role of its own rather than a
118
+ // property of the node it labels: on this canvas a label IS a separate free
119
+ // text element, grouped with its node, and W3 is about where it lands.
120
+ //
121
+ // `text`, not `node`: a label is created 120 to 200 units wide whatever it
122
+ // reads, so its BOX is not a statement about anything the eye can see. The
123
+ // kind is what tells a rule to measure the words instead.
124
+ {
125
+ id: WARDLEY_ROLE.label,
126
+ kind: 'text',
127
+ labelKey: 'com.labre.wardley.role.label',
128
+ },
129
+ ];
130
+ // Null prototype: this is a lookup table keyed by ids that may one day come
131
+ // from host-supplied packs, so `defs['toString']` must not resolve.
132
+ export const WARDLEY_ROLES = Object.assign(Object.create(null), Object.fromEntries(DEFS.map(def => [def.id, def])));
@@ -0,0 +1,2 @@
1
+ import type { ValidationRule } from '@formicoidea/labre-core/blocks/surface';
2
+ export declare const WARDLEY_RULES: readonly ValidationRule[];