@formicoidea/labre-framework-wardley 0.30.1 → 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.
- package/dist/actions.d.ts +14 -18
- package/dist/actions.js +80 -39
- package/dist/audit-criteria.d.ts +31 -0
- package/dist/audit-criteria.js +90 -0
- package/dist/background.d.ts +14 -0
- package/dist/background.js +338 -0
- package/dist/commands.d.ts +5 -0
- package/dist/commands.js +190 -0
- package/dist/consts.d.ts +10 -4
- package/dist/consts.js +10 -9
- package/dist/descriptor.d.ts +8 -3
- package/dist/descriptor.js +6 -3
- package/dist/effects.d.ts +2 -2
- package/dist/effects.js +2 -2
- package/dist/element-renderer.d.ts +8 -5
- package/dist/element-renderer.js +12 -147
- package/dist/element-view.d.ts +8 -4
- package/dist/element-view.js +30 -22
- package/dist/gradient.d.ts +6 -11
- package/dist/gradient.js +58 -47
- package/dist/index.d.ts +9 -1
- package/dist/index.js +9 -1
- package/dist/legend.js +10 -2
- package/dist/natures.d.ts +50 -0
- package/dist/natures.js +93 -0
- package/dist/node/node-renderer.js +1 -1
- package/dist/nudges.d.ts +41 -0
- package/dist/nudges.js +69 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +87 -0
- package/dist/reading.d.ts +3 -0
- package/dist/reading.js +129 -0
- package/dist/roles.d.ts +50 -0
- package/dist/roles.js +132 -0
- package/dist/rules.d.ts +2 -0
- package/dist/rules.js +270 -0
- package/dist/templates/index.js +54 -10
- package/dist/templates/maps.js +122 -18
- package/dist/toolbar/config.js +2 -2
- package/dist/toolbar/wardley-menu.d.ts +8 -15
- package/dist/toolbar/wardley-menu.js +8 -136
- package/dist/toolbar/wardley-senior-button.js +1 -1
- package/dist/translations.d.ts +16 -0
- package/dist/translations.js +24 -0
- package/dist/view.d.ts +17 -0
- package/dist/view.js +125 -22
- package/package.json +2 -2
- package/dist/label-layout.d.ts +0 -20
- package/dist/label-layout.js +0 -72
- package/dist/shortcuts.d.ts +0 -2
- package/dist/shortcuts.js +0 -37
package/dist/nudges.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { QualityNudge } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
/**
|
|
3
|
+
* Wardley **map quality** — the checklist (PF13.9).
|
|
4
|
+
*
|
|
5
|
+
* Level 1 is `./rules.ts`: deterministic, decidable, real time. This file is
|
|
6
|
+
* level 2, and the split is the whole point of the taxonomy — a rule only enters
|
|
7
|
+
* the deterministic engine if an algorithm can decide it on persisted data
|
|
8
|
+
* inside the ~16 ms budget. Everything else is a NUDGE: an expectation offered
|
|
9
|
+
* as a checklist, never evaluated, never blocking anything. Ticking is assuming.
|
|
10
|
+
*
|
|
11
|
+
* ## The check-up rules are gone (PO decision, 02/08/2026)
|
|
12
|
+
*
|
|
13
|
+
* Q5 (tone convention) and Q6 (phase nomenclature) used to live beside these,
|
|
14
|
+
* as on-demand rules behind a "Run check-up" button. The panel is now the
|
|
15
|
+
* checklist and nothing else: the PO's recette found that a second verdict, a
|
|
16
|
+
* button and a scope line asked the reader to hold three different kinds of
|
|
17
|
+
* statement in their head over a map that mostly wanted a title. Nothing was
|
|
18
|
+
* deleted from the platform — the on-demand MOMENT (PF5.14) and the
|
|
19
|
+
* `tone-convention` / `majority-fact` families are still in the engine, still
|
|
20
|
+
* tested, and the next framework that wants a check-up declares one. Wardley
|
|
21
|
+
* simply stopped exposing one.
|
|
22
|
+
*
|
|
23
|
+
* Registered from the flag-gated `WardleyViewExtension`, beside the rules and
|
|
24
|
+
* the profiles: a checklist is tooling. Switching the flag off takes it away and
|
|
25
|
+
* leaves the ticks written on the map, unread, until it comes back (PF7.10).
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* **Q1–Q4** — the four things a Wardley map needs in order to do its job, and
|
|
29
|
+
* that no algorithm can check.
|
|
30
|
+
*
|
|
31
|
+
* Every one of them is about whether the map can be DISCUSSED. A map with no
|
|
32
|
+
* title is a diagram nobody can situate; one with no legend is a private
|
|
33
|
+
* notation; one whose evolution axis is drawn but not used is a value chain
|
|
34
|
+
* wearing a Wardley costume. The tool can put all four on screen and it cannot
|
|
35
|
+
* judge a single one — "the title contextualises the study" is a statement about
|
|
36
|
+
* meaning, and a rule claiming to have verified it would be lying.
|
|
37
|
+
*
|
|
38
|
+
* The wordings are the PO's own, from the review of 01/08/2026, carried as
|
|
39
|
+
* `fallback` so a host with no catalogue reads them exactly as written.
|
|
40
|
+
*/
|
|
41
|
+
export declare const WARDLEY_NUDGES: readonly QualityNudge[];
|
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
|
+
];
|
package/dist/profiles.js
ADDED
|
@@ -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];
|
package/dist/reading.js
ADDED
|
@@ -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
|
+
};
|
package/dist/roles.d.ts
ADDED
|
@@ -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])));
|
package/dist/rules.d.ts
ADDED