@owlmeans/viable-common 0.0.39 → 0.0.41
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/README.md +4 -4
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/agent-presentation/SKILL.md +1 -1
- package/agent-meta/skills/viable-common/SKILL.md +331 -391
- package/build/areas/index.d.ts +1 -0
- package/build/areas/index.d.ts.map +1 -1
- package/build/areas/index.js +1 -0
- package/build/areas/index.js.map +1 -1
- package/build/areas/tenancy.d.ts +55 -0
- package/build/areas/tenancy.d.ts.map +1 -0
- package/build/areas/tenancy.js +33 -0
- package/build/areas/tenancy.js.map +1 -0
- package/build/blueprint/types.d.ts +19 -1
- package/build/blueprint/types.d.ts.map +1 -1
- package/build/blueprint/types.js.map +1 -1
- package/build/connect/consts.d.ts +3 -15
- package/build/connect/consts.d.ts.map +1 -1
- package/build/connect/consts.js +3 -15
- package/build/connect/consts.js.map +1 -1
- package/build/connect/entrypoints.d.ts +33 -69
- package/build/connect/entrypoints.d.ts.map +1 -1
- package/build/connect/entrypoints.js +5 -25
- package/build/connect/entrypoints.js.map +1 -1
- package/build/connect/references.d.ts +1 -31
- package/build/connect/references.d.ts.map +1 -1
- package/build/connect/references.js +0 -7
- package/build/connect/references.js.map +1 -1
- package/build/connect/types.d.ts +1 -25
- package/build/connect/types.d.ts.map +1 -1
- package/build/dev/consts.d.ts +15 -0
- package/build/dev/consts.d.ts.map +1 -1
- package/build/dev/consts.js +16 -0
- package/build/dev/consts.js.map +1 -1
- package/build/dev/schemas.d.ts +7 -0
- package/build/dev/schemas.d.ts.map +1 -1
- package/build/dev/schemas.js +9 -1
- package/build/dev/schemas.js.map +1 -1
- package/build/dev/types.d.ts +15 -1
- package/build/dev/types.d.ts.map +1 -1
- package/build/planning/schemas.d.ts.map +1 -1
- package/build/planning/schemas.js +25 -0
- package/build/planning/schemas.js.map +1 -1
- package/build/planning/types.d.ts +9 -0
- package/build/planning/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/areas/index.ts +1 -0
- package/src/areas/tenancy.ts +69 -0
- package/src/blueprint/types.ts +19 -1
- package/src/connect/consts.ts +3 -16
- package/src/connect/entrypoints.ts +10 -60
- package/src/connect/references.ts +4 -26
- package/src/connect/types.ts +1 -22
- package/src/dev/consts.ts +16 -0
- package/src/dev/schemas.ts +13 -1
- package/src/dev/types.ts +15 -1
- package/src/planning/schemas.ts +25 -0
- package/src/planning/types.ts +9 -0
- package/tests/access-schema.spec.ts +37 -0
- package/tests/connect-convert.spec.ts +5 -11
- package/tests/connect-entrypoints.spec.ts +33 -14
- package/tests/planning.spec.ts +26 -0
- package/tests/tenancy.spec.ts +48 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: viable-common
|
|
3
|
-
description: How to use @owlmeans/viable-common — the runtime-free contract package of the OwlMeans Viable platform. Covers planning cards and flows (including the landing-gate fields), project/story refusals, slot commands and layouts, slot metadata keys, target integrity, connector domain statuses and routes, the intent-first hand-off (`./intent`), conversion vocabulary, blueprint layer types and the case vocabulary, the ViableSkill/ViablePersona names (never the catalogue behind them), what belongs here versus in @owlmeans/viable, generated-project analysis/design/scaffold/metadata shapes, StoryDesignPort, schema conventions, and wire-version rules. Auto-invoked when importing a viable card type or flow, a slot command, a connector or conversion type, a target-integrity helper, or any *Schema this package exports.
|
|
3
|
+
description: How to use @owlmeans/viable-common — the runtime-free contract package of the OwlMeans Viable platform. Covers planning cards and flows (including the landing-gate and tenancy fields), project/story refusals, slot commands and layouts, slot metadata keys, target integrity, connector domain statuses and routes, the intent-first hand-off (`./intent`), conversion vocabulary, blueprint layer types and the case vocabulary, the ViableSkill/ViablePersona names (never the catalogue behind them), what belongs here versus in @owlmeans/viable, generated-project analysis/design/scaffold/metadata shapes, StoryDesignPort, schema conventions, and wire-version rules. Auto-invoked when importing a viable card type or flow, a slot command, a connector or conversion type, a target-integrity helper, or any *Schema this package exports.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
@@ -8,103 +8,84 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/viable-common
|
|
9
9
|
|
|
10
10
|
**Layer:** Cross-cutting domain (contracts only)
|
|
11
|
-
**Install:** `"@owlmeans/viable-common": "^0.0.
|
|
12
|
-
**Subpaths:** `.` · `./slot` · `./connect` · `./convert` · `./integrity` · `./intent`
|
|
11
|
+
**Install:** `"@owlmeans/viable-common": "^0.0.41"` in `dependencies`
|
|
12
|
+
**Subpaths:** `.` · `./slot` · `./connect` · `./convert` · `./integrity` · `./intent` — the barrel
|
|
13
|
+
re-exports every subpath except `./intent`.
|
|
13
14
|
**Runtime-free:** no `@langchain/*`, no filesystem, no Ajv at run time (a devDependency, for the
|
|
14
|
-
tests that compile the schemas)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
instead: the skill bodies and their order, the persona prompt
|
|
29
|
-
policies, the blueprint case table and resolution, the BA model-answer schemas. What stays here is
|
|
30
|
-
what another package's contract references by type — the `ViableSkill` / `ViablePersona` enums and
|
|
31
|
-
the `Blueprint` types and vocabulary that execution state and card fields name.
|
|
15
|
+
tests that compile the schemas); it depends on the packages under Depends On and nothing else.
|
|
16
|
+
|
|
17
|
+
Every name the Viable platform puts on a wire, on a volume or in a prompt is declared here once,
|
|
18
|
+
and the runtimes that must agree about it — the platform's API services, the agent, the
|
|
19
|
+
publisher/production runtime and the connector SDK on somebody's laptop — read the same
|
|
20
|
+
declaration. A vocabulary copied instead of imported is how one tree gets two totals, one refusal
|
|
21
|
+
two spellings and one ceiling two values.
|
|
22
|
+
|
|
23
|
+
**Names, not agent-only data.** The BROWSER imports this package, so everything in it ships in
|
|
24
|
+
every visitor's bundle. Data only the AI agent library reads — skill bodies and their order,
|
|
25
|
+
persona prompt policies, the blueprint case table and its resolution, the BA model-answer schemas
|
|
26
|
+
— belongs in `@owlmeans/viable`. What stays here is what another package's contract references by
|
|
27
|
+
type: the `ViableSkill` / `ViablePersona` enums and the `Blueprint` types and vocabulary that
|
|
28
|
+
execution state and card fields name.
|
|
32
29
|
|
|
33
30
|
## Key Exports
|
|
34
31
|
|
|
35
32
|
| Subpath | What it declares |
|
|
36
33
|
|---|---|
|
|
37
|
-
| `.` (barrel) | The planning module (`VIABLE_*_TYPE`, `VIABLE_TYPE_SCHEMAS`, `VIABLE_FLOW_SCHEMAS`, `ViableStoryStatus`/`ViableProjectStatus` and their transitions, `ViableSpecCategory`, `ViableRelationship`, `ViableChannel`, `ViableProjectCard`/`ViableStoryCard`, the card helpers, the landing sentence helpers, the `Project*` refusals); `SlotMetadata` and the three metadata vocabularies (`metadataConfigs`, `metadataLists`, `metadataSecrets`), `BRANDING_ENV_KEYS` / `brandingEnv`; `ProjectArea` / `AREA_PATHS` / `AREA_ACCESS` / `AREA_TIER`; `ModelRole` and the viable `ExecutionState`; the `ViableSkill` / `ViablePersona` enums
|
|
38
|
-
| `./slot` | `SlotCommandType` and the `SlotFileCommand` / `SlotShellCommand` / `SlotGitCommand` sets, `SubProject`, `WorkloadKind`, the target ports and process markers, `slotOrigin` / `targetRedirectUrisForOrigin` |
|
|
34
|
+
| `.` (barrel) | The planning module (`VIABLE_*_TYPE`, `VIABLE_TYPE_SCHEMAS`, `VIABLE_FLOW_SCHEMAS`, `ViableStoryStatus`/`ViableProjectStatus` and their transitions, `ViableSpecCategory`, `ViableRelationship`, `ViableChannel`, `ViableProjectCard`/`ViableStoryCard`, the card helpers, the landing sentence helpers, the `Project*` refusals); `SlotMetadata` and the three metadata vocabularies (`metadataConfigs`, `metadataLists`, `metadataSecrets`), `BRANDING_ENV_KEYS` / `brandingEnv`; `ProjectArea` / `AREA_PATHS` / `AREA_ACCESS` / `AREA_TIER`, `ADMIN_PERMISSION` / `OPERATOR_PERMISSION`; the tenancy contract (`ProjectTenancy`, `ViableTenancyDecision`, `NO_TENANCY`, `tenancyOf`, `tenantedArea`, `TENANCY_QUOTE_MAX`); `ModelRole` and the viable `ExecutionState`; the `ViableSkill` / `ViablePersona` enums; the `Blueprint` layer types, `BlueprintRef` / `BlueprintPatch`, `BlueprintCase` / `GameKind`, `DEFAULT_BLUEPRINT_ID` / `BLUEPRINT_META_KEY`, `landingGatePreferenceOf`; the target topology (`TopologyDescriptor`, `LAYOUT_TOPOLOGIES`, `resolveTopology`, `packageForRole` — meaning in `/blueprints`); the BA shapes (`mergeConnectingStories`), the dev (`AccessBlock`, `AccessLevel`, `PermissionDefault`), UX, design and scaffold shapes and their schemas, `StoryDesignPort`; `ModerationCategory` / `ModerationSubject` / `decideModeration`; the `docs/` metadata paths; `PreviewEventType`; the agent-output taxonomy (`classifyAgentMessage`, `isAgentMessageHidden` — `/agent-presentation`) and the spectator entry types |
|
|
35
|
+
| `./slot` | `SlotCommandType` and the `SlotFileCommand` / `SlotShellCommand` / `SlotGitCommand` sets, the per-command deadlines and timeouts (`commandDeadline`, `commandTimeout`), `SubProject`, `LAYOUTS` / `ROLE_DIRS` / `subprojectDirOf`, `WorkloadKind`, the target ports and process markers, `slotOrigin` / `targetRedirectUrisForOrigin` |
|
|
39
36
|
| `./connect` | `ConnectTarget`, `ConnectLlm`, `ConnectHarness`, `ConnectExecutor`, `ConnectOpKind`, `ConnectProjectStatus`, `ConnectStoryStatus`, `ConnectPipelineState`, `ConnectWaitReason`, `ConnectProjectBranding` / `ConnectProjectBrandingSave`, `ModelTier` + `tierOfRole`/`clampTier`, `ModelTask*`, `InquiryPayload` + `ConnectInquiryKind`, the session and domain-status views, the `Connect*` error family, `connectProtocols(opts)` and every `*Schema` behind them |
|
|
40
|
-
| `./convert` | `ConversionStage`/`Status`/`Decision` and the `stageAfter`/`decisionFor`/`canEnter` transitions, `OriginKind`/`
|
|
37
|
+
| `./convert` | `ConversionStage`/`Status`/`Decision` and the `stageAfter`/`decisionFor`/`canEnter` transitions, `OriginKind`/`OriginShape`/`OriginState`, `StackId` + `STACK_FAMILY`, `ArchitectureCase`, `ConvertibilityVerdict`/`ConvertibilityReason`, the census classifiers (`fileClassOf`, `sizeClassOf`, `entropyClassOf`, `binaryByExtension`), the `docs/conversion/` paths, `CONVERTED_ORIGIN_DIR`, `SOURCE_LIST_EXCLUSIONS`, `CENSUS_SKIP_DIRS`, `RELOCATE_ALWAYS_KEEP`, and the model-answer schemas the conversion asks with |
|
|
41
38
|
| `./integrity` | `TargetLayout` + `TARGET_LAYOUTS`, `detectTargetLayout`, `verifyTargetShape`, `TARGET_INTEGRITY_FILES`, `TARGET_PROTECTED_FILES`, `isLegacyLayout`, `targetPackageName` |
|
|
42
|
-
| `./intent` |
|
|
39
|
+
| `./intent` | `intent` (the four aliases), `makeIntentProtocols(opts?)`, `intentFlow` + `IntentFlowStep` + `INTENT_PAYLOAD_REF`, `IntentStashBodySchema` / `IntentPickupBodySchema`, the `INTENT_*` constants, `IntentDraft`, `IntentExpired` (404) / `IntentThrottled` (429) |
|
|
43
40
|
|
|
44
41
|
## The planning module
|
|
45
42
|
|
|
46
43
|
A Viable project, its user stories and the documents behind them are `@owlmeans/planning`
|
|
47
|
-
WORKCARDS. The planning packages own the record,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
`VIABLE_FLOW_SCHEMAS`; nothing redeclares a type or a flow locally.
|
|
44
|
+
WORKCARDS. The planning packages own the record, transition, fold, stores and protocol tree
|
|
45
|
+
(`/planning`); this module owns what a Viable card IS — its type, flow, `fields`, slots and code.
|
|
46
|
+
A plugin registers `VIABLE_TYPE_SCHEMAS` and `VIABLE_FLOW_SCHEMAS`; nothing redeclares a type or a
|
|
47
|
+
flow locally.
|
|
52
48
|
|
|
53
49
|
### Types
|
|
54
50
|
|
|
55
51
|
| Type | Kind | Primary flow | Code | May hold |
|
|
56
52
|
|---|---|---|---|---|
|
|
57
|
-
| `viable:project` (`VIABLE_PROJECT_TYPE`) | project | `viable:project` | slug, unique in the
|
|
53
|
+
| `viable:project` (`VIABLE_PROJECT_TYPE`) | project | `viable:project` | slug, unique in the organization, mutable (rename, host ladder) | `viable:user-story` |
|
|
58
54
|
| `viable:user-story` (`VIABLE_STORY_TYPE`) | card | `viable:user-story` | `US-` + 5 uppercase, unique in the project, never changes | — |
|
|
59
55
|
| `viable:spec` (`VIABLE_SPEC_TYPE`) | specification | `viable:spec` (one status, `current`) | none | — |
|
|
60
56
|
| `viable:bug` / `viable:improvement` / `viable:requirement` | card | `viable:user-story` | `BUG-` / `IMP-` / `REQ-` | — |
|
|
61
57
|
|
|
62
58
|
The three RESERVED types are declared in full so their prefix and flow are decided once, and are
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
absent from the project type's `cardTypes`: nothing creates one until a feature adds it there. A
|
|
60
|
+
type always runs a flow — the planning executor has no status for a flowless card, so a document
|
|
61
|
+
runs the one-status `viable:spec` flow.
|
|
66
62
|
|
|
67
63
|
### The two flows
|
|
68
64
|
|
|
69
|
-
Story flow — the keys are
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
| Status | Intrinsic | Tone |
|
|
74
|
-
|---|---|---|
|
|
75
|
-
| `planned` (initial) | planned | blue |
|
|
76
|
-
| `in-progress` | in-progress | yellow |
|
|
77
|
-
| `completed` | closed | green |
|
|
78
|
-
| `failed` | **planned** | red |
|
|
65
|
+
Story flow — the keys are wire contracts (the `docs/stories/<code>.md` frontmatter, the
|
|
66
|
+
connector's story-status mapping, the board columns, their i18n keys, every e2e selector), and a
|
|
67
|
+
key is never renamed.
|
|
79
68
|
|
|
80
|
-
| Transition | From | To |
|
|
81
|
-
|
|
82
|
-
| `start` | `planned`, `failed` | `in-progress` |
|
|
83
|
-
| `complete` | `in-progress` | `completed` |
|
|
84
|
-
| `fail` | `in-progress` | `failed` |
|
|
85
|
-
| `reset` | `*` | `planned` |
|
|
69
|
+
| Status | Intrinsic | Tone | | Transition | From | To |
|
|
70
|
+
|---|---|---|---|---|---|---|
|
|
71
|
+
| `planned` (initial) | planned | blue | | `start` | `planned`, `failed` | `in-progress` |
|
|
72
|
+
| `in-progress` | in-progress | yellow | | `complete` | `in-progress` | `completed` |
|
|
73
|
+
| `completed` | closed | green | | `fail` | `in-progress` | `failed` |
|
|
74
|
+
| `failed` | **planned** | red | | `reset` | `*` | `planned` |
|
|
86
75
|
|
|
87
76
|
`failed` is intrinsically planned: a failed story is work still to do, so "done" is the closed
|
|
88
|
-
count
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
Project flow:
|
|
92
|
-
|
|
93
|
-
| Status | Intrinsic | Tone |
|
|
94
|
-
|---|---|---|
|
|
95
|
-
| `draft` (initial) | planned | blue |
|
|
96
|
-
| `confirmed` | in-progress | yellow |
|
|
97
|
-
| `active` | in-progress | green |
|
|
98
|
-
| `archived` | closed | neutral |
|
|
77
|
+
count and a retry is `start` again. `reset` is the manual recovery and the only move a completed
|
|
78
|
+
story accepts.
|
|
99
79
|
|
|
100
|
-
`
|
|
101
|
-
`
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
80
|
+
Project flow: `draft` (initial; planned, blue), `confirmed` (in-progress, yellow), `active`
|
|
81
|
+
(in-progress, green), `archived` (closed, neutral); `confirm` (`draft → confirmed`), `activate`
|
|
82
|
+
(`confirmed → active`), `archive` (`* → archived`), `reopen` (`archived → active`). A create lands
|
|
83
|
+
at `draft`, confirming the brief is `confirm`, the end of an initialization is `activate`. A
|
|
84
|
+
reinitialization and a failed initialization are facts of the SLOT, not transitions; destroying a
|
|
85
|
+
project is a `delete` plus the platform's cascade, never `archive`.
|
|
105
86
|
|
|
106
|
-
Every status carries a `tone` (`ViableTone`): a board draws its columns and
|
|
107
|
-
|
|
87
|
+
Every status carries a `tone` (`ViableTone`): a board draws its columns and palette from the flow,
|
|
88
|
+
never from a local table.
|
|
108
89
|
|
|
109
90
|
### Where each fact lives on a card
|
|
110
91
|
|
|
@@ -115,48 +96,63 @@ the wire and in target files — a parent agent's vocabulary, which never change
|
|
|
115
96
|
| Fact | On the card |
|
|
116
97
|
|---|---|
|
|
117
98
|
| project `id` / story `id` | `id` (a card id) |
|
|
118
|
-
| project `alias` | `code` |
|
|
119
|
-
| project `name` | `title` |
|
|
120
|
-
| project `description` | `description` |
|
|
99
|
+
| project `alias` / `name` / `description` | `code` / `title` / `description` |
|
|
121
100
|
| project `specification` / `vision` / `designSystem` | specification bodies, categories `specification` / `vision` / `design-system` |
|
|
122
101
|
| project `formerAliases`, `language`, `blueprint`, `blueprintCase`, `gameKind`, `target`, `origin`, `connectLlmMode`, `converterLlmMode` | `fields.*` |
|
|
123
102
|
| the landing-gate decision | project `fields.landing` — `{ story: code \| null, at }` |
|
|
124
|
-
|
|
|
125
|
-
| story `code` | `code` |
|
|
126
|
-
| story `status` | `status` (same strings) |
|
|
103
|
+
| the tenancy decision | project `fields.tenancy` — `ViableTenancyDecision` `{ operators, users, quotes?, by: 'model' \| 'owner', at }` |
|
|
104
|
+
| story narrative (`story`) / `code` / `status` | `title` / `code` / `status` (same strings) |
|
|
127
105
|
| story `projectId` | `parent` |
|
|
128
106
|
| story position in the flow | `order` — a double; a connective story sits between two flow steps (`3.5`) |
|
|
129
|
-
| story `area`, `primary`, `actor`, `warning`, `kind`, `landing` | `fields.*`
|
|
130
|
-
| a story's design | specification `design` under the STORY card |
|
|
131
|
-
| the scaffold plan | specification `scaffold` under the PROJECT card |
|
|
107
|
+
| story `area`, `primary`, `actor`, `warning`, `kind`, `landing` | `fields.*` |
|
|
108
|
+
| a story's design / the scaffold plan | specification `design` under the STORY card / `scaffold` under the PROJECT card |
|
|
132
109
|
|
|
133
|
-
`area` and `primary` are required in `ViableStoryFieldsSchema`; a create
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
110
|
+
`area` and `primary` are required in `ViableStoryFieldsSchema`; a create without an area still
|
|
111
|
+
validates, because validation runs after the planning middlewares and the viable plugin fills the
|
|
112
|
+
area of a narrative a person wrote. `storyFieldsOf` answers an absent area as `guest` and an
|
|
113
|
+
absent flag as `false`.
|
|
137
114
|
|
|
138
115
|
### The landing gate on the cards
|
|
139
116
|
|
|
140
117
|
The landing gate is the key end-user story a guest starts on the landing page and continues on its
|
|
141
|
-
full-scale screen after signing in. Two fields record it
|
|
142
|
-
|
|
143
|
-
- project `fields.landing` is the DECISION, in three states: absent — never decided
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
118
|
+
full-scale screen after signing in (`/target-areas`). Two fields record it:
|
|
119
|
+
|
|
120
|
+
- project `fields.landing` is the DECISION, in three states: absent — never decided;
|
|
121
|
+
`{ story: null, at }` — decided, no gate; `{ story: <code>, at }` — that story. A failed decision
|
|
122
|
+
is never written, since `null` would read as a decided "none" that nothing asks again. `at`
|
|
123
|
+
carries no `format` because the planning registry compiles without ajv-formats.
|
|
124
|
+
- story `fields.landing: true` marks the chosen card — at most one per project — and is what design
|
|
125
|
+
and development key on. `storyWriteInputOf` copies it into `StoryWriteInput.landing` only when
|
|
126
|
+
set, so `StoryMeta.landing` appears only in that story's `docs/stories/<code>.md`; like `primary`
|
|
127
|
+
it is the CARD's and is never passed in `StoryWriteContent`.
|
|
128
|
+
|
|
129
|
+
`withLandingSentence` appends `LANDING_STORY_SENTENCE` to the chosen narrative — a note for people
|
|
130
|
+
reading the board, never the authority. It is idempotent (`hasLandingSentence`,
|
|
131
|
+
whitespace-insensitive), joins with one space, and never pushes a title past `TITLE_MAX`: a
|
|
132
|
+
narrative with no room is returned uncut.
|
|
133
|
+
|
|
134
|
+
### The tenancy decision on the card
|
|
135
|
+
|
|
136
|
+
`ProjectTenancy` is two independent flags: `operators` (the back office's staff split into tenant
|
|
137
|
+
organizations) and `users` (end users distributed across tenant organizations). An organization is
|
|
138
|
+
an IAM-managed tenant, never a table the target keeps; both flags off is a single-organization
|
|
139
|
+
application, the project owner's, that every person acts in.
|
|
140
|
+
|
|
141
|
+
- project `fields.tenancy` (`ViableTenancyDecision`): ABSENT means never decided and reads as
|
|
142
|
+
single-organization; a recorded one always carries BOTH flags, `by` (`'model'` inferred,
|
|
143
|
+
`'owner'` set) and `at`, and may carry `quotes` — the requester's own sentences, verbatim, each
|
|
144
|
+
at most `TENANCY_QUOTE_MAX` (1024) characters. A partial decision, another decider or a stray key
|
|
145
|
+
is refused.
|
|
146
|
+
- It reaches a run only through `BlueprintRef.tenancy`, and only when a flag is on; resolution
|
|
147
|
+
alone writes it into `experience.tenancy` — never a blueprint or a case table. Read it through
|
|
148
|
+
`tenancyOf(blueprint)`: `NO_TENANCY` (frozen, both off) for no blueprint, layer, key or an
|
|
149
|
+
unreadable value — only a literal `true` turns a flag on.
|
|
150
|
+
- `tenantedArea(area, tenancy)` — `user` follows `users`, `operator` follows `operators`; `guest`
|
|
151
|
+
and `admin` (the project owner, above every tenant) are never tenanted.
|
|
152
|
+
- Classification, the access policy, per-record kinds and the IAM client config live in
|
|
153
|
+
`@owlmeans/viable` (`/target-tenancy`).
|
|
154
|
+
|
|
155
|
+
### Slots, codes, relationships
|
|
160
156
|
|
|
161
157
|
| Card | Category | Format | Rule |
|
|
162
158
|
|---|---|---|---|
|
|
@@ -166,369 +162,313 @@ note for the people reading the board, never the authority. It is idempotent (ch
|
|
|
166
162
|
| story | `design` | json | revisioned, keeps 3, body is a `StoryDesign` at `STORY_DESIGN_VERSION` |
|
|
167
163
|
|
|
168
164
|
The co-located `*.spec.md` / `*.ux.md` / `*.ui.md` files of a target are FILES, never slots — that
|
|
169
|
-
prose
|
|
170
|
-
type declares a `ux` or `ui` slot.
|
|
171
|
-
|
|
172
|
-
### Codes
|
|
165
|
+
prose lives in the `design` payload — so no type declares a `ux` or `ui` slot.
|
|
173
166
|
|
|
174
167
|
A story CODE (`US-XXXXX`) is the target's vocabulary: every file, registry entry and widget stamp
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
(which knows retired aliases and refused hostnames); the slug policy only guards uniqueness.
|
|
179
|
-
|
|
180
|
-
### Relationships
|
|
168
|
+
names the code, and a card id never reaches a target file — `storyWriteInputOf` refuses a card
|
|
169
|
+
with no code (`ProjectStoryMissconfigured('no-code')`). A project's code is its alias, minted by
|
|
170
|
+
the platform's name ladder; the slug policy only guards uniqueness.
|
|
181
171
|
|
|
182
|
-
`follows`, `shares-widget` and `shares-screen` run story → story
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
declared only — the `scaffold` specification is their single authority, and links would be a
|
|
187
|
-
second answer that can disagree with it.
|
|
172
|
+
`follows`, `shares-widget` and `shares-screen` run story → story. Only `follows` is WRITTEN: a
|
|
173
|
+
connective story follows the flow story it was anchored after, created in the same transition from
|
|
174
|
+
`MergedStoryDraft.after` (the clamped 1-based position, filled by `mergeConnectingStories` alone).
|
|
175
|
+
The `shares-*` types are declared only — the `scaffold` specification is their single authority.
|
|
188
176
|
|
|
189
177
|
### The write channel
|
|
190
178
|
|
|
191
179
|
`ViableChannel` (`web`, `connect`, `agent`, `pipeline`) is what a planning write carries on
|
|
192
|
-
`PlanningScope.channel` and records on `actor.channel`. It is never read from the wire: the
|
|
193
|
-
|
|
194
|
-
writes are `agent` or `pipeline`. Three rules
|
|
180
|
+
`PlanningScope.channel` and records on `actor.channel`. It is never read from the wire: the API
|
|
181
|
+
services derive `connect` from an access-token request and `web` from everything else; the agent's
|
|
182
|
+
own writes are `agent` or `pipeline`. Three rules ask `isUserChannel`:
|
|
195
183
|
|
|
196
184
|
- the balance refusal — `ConnectOutOfCredits` for `connect`, `AgentOutOfTokens` otherwise;
|
|
197
|
-
- re-formatting a narrative through the analyst — `web`/`connect` only
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
never the `start` a conversion or free flight writes for itself.
|
|
185
|
+
- re-formatting a narrative through the analyst — `web`/`connect` only;
|
|
186
|
+
- the develop trigger — only a committed `start` written by `web`/`connect`, never the `start` a
|
|
187
|
+
conversion or free flight writes for itself.
|
|
201
188
|
|
|
202
|
-
### Card helpers
|
|
189
|
+
### Card helpers and the design port
|
|
203
190
|
|
|
204
191
|
`isViableProject` / `isViableStory` check kind AND type. `projectBriefOf(project, specs)` assembles
|
|
205
|
-
the `AgentProject` brief
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
`
|
|
209
|
-
`
|
|
210
|
-
`TaskExecutionState.card` carry the cards as plain data beside the brief and the aggregate.
|
|
211
|
-
|
|
212
|
-
### The design port
|
|
192
|
+
the `AgentProject` brief (highest revision per part, `''` for an unwritten part, `alias` from the
|
|
193
|
+
code). `userStoryOf(card, design?)` builds the coders' aggregate — the card wins on narrative,
|
|
194
|
+
code, area and actor, the design supplies entities and screens; `userStoryOfDesign(design)` is the
|
|
195
|
+
design-only reading. `storyDraftOf(card)` feeds the analysis prompts and moderation.
|
|
196
|
+
`ExecutionState.projectCard` and `TaskExecutionState.card` carry the cards as plain data.
|
|
213
197
|
|
|
214
|
-
`StoryDesignPort` is
|
|
215
|
-
`put(cardId, design, { runId?, cause? })` answering the revision. The port
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
and never addresses a record.
|
|
198
|
+
`StoryDesignPort` is addressed by CARD id — `current(cardId)` and
|
|
199
|
+
`put(cardId, design, { runId?, cause? })` answering the revision. The port picks the slot (`design`
|
|
200
|
+
for a story, `scaffold` for the project) and answers `null` for a stored design of another
|
|
201
|
+
version; the code inside a design never addresses a record.
|
|
219
202
|
|
|
220
203
|
## A schema here is written for the reader that will refuse it
|
|
221
204
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
`
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
faults is particular to a conversion and a walk over one directory lets the same shape through
|
|
239
|
-
in a design, a slot or a connector schema:
|
|
240
|
-
|
|
241
|
-
- **`nullable: true` always carries its `type`.** Ajv refuses `nullable` on its own, and a schema
|
|
242
|
-
that fails to compile takes down whatever compiled it — the route registration, the collection
|
|
243
|
-
validator — instead of the one call that would have carried the value.
|
|
244
|
-
- **A nullable ENUM lists `null` among its values**: `enum: [...Object.values(X), null]`.
|
|
245
|
-
`nullable` and `enum` are separate keywords checked independently, so the pair written apart
|
|
246
|
-
widens the type check to admit `null` and then refuses that very value by enum — the field can
|
|
247
|
-
only ever be OMITTED, never sent empty. Nothing fails at compile time and nothing fails for a
|
|
248
|
-
caller that leaves the key out, which is why it survives review; it surfaces as the one caller
|
|
249
|
-
that spells absence as `null`. Both callers exist here: a provider's structured output writes an
|
|
250
|
-
unset optional as `null` (an intake stack call was retried three times and discarded as
|
|
251
|
-
`llm:retry-exceeded`), and a wire body where `null` MEANS something — `llmMode`, "inherit the
|
|
252
|
-
profile's setting" — has no other spelling at all.
|
|
253
|
-
- **`items` is never an array.** A draft-04 tuple is refused wherever the schema reaches a
|
|
254
|
-
provider or a collection validator.
|
|
205
|
+
**Model-answer schemas** — the shape of a model's single JSON object — are annotated
|
|
206
|
+
`JSONSchemaType<T>` (checked, not cast), because the answer is parsed straight back into that type.
|
|
207
|
+
|
|
208
|
+
**Record-element schemas** — what the platform stores and puts on the wire — are hand-written and
|
|
209
|
+
cast, because they are re-applied as a Mongo `$jsonSchema`, stricter than Ajv: no `Record<>` maps,
|
|
210
|
+
no `integer` (a stored number is a double), dates as ISO strings.
|
|
211
|
+
|
|
212
|
+
Three rules hold for both; `tests/convert.spec.ts` walks the PACKAGE barrel for each:
|
|
213
|
+
|
|
214
|
+
- **`nullable: true` always carries its `type`.** Ajv refuses `nullable` alone, and a schema that
|
|
215
|
+
fails to compile takes down whatever compiled it (route registration, collection validator).
|
|
216
|
+
- **A nullable ENUM lists `null` among its values**: `enum: [...Object.values(X), null]`. Written
|
|
217
|
+
apart, the type check admits `null` and the enum refuses it, so the field can only be omitted.
|
|
218
|
+
Both kinds of caller send `null`: a provider's structured output for an unset optional, and a
|
|
219
|
+
wire body where `null` MEANS something (`llmMode` — "inherit the profile's setting").
|
|
220
|
+
- **`items` is never an array.** A draft-04 tuple is refused by providers and collection validators.
|
|
255
221
|
|
|
256
222
|
**A schema that validates a STORED document only grows optional.** `ScaffoldPlanSchema` is both a
|
|
257
|
-
model-answer schema and the
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
platform, so `ConnectCapabilitiesSchema.executors.items`
|
|
268
|
-
is a bare string: a newer executor kind must stay an unused capability on an older platform, never
|
|
269
|
-
a refused session. Apply the same reasoning to anything else a newer connector may send an older
|
|
270
|
-
platform, and nowhere else — a closed set is worth more than a tolerant one everywhere both ends
|
|
271
|
-
deploy together.
|
|
223
|
+
model-answer schema and the `scaffold` slot's validator, so an older plan must still pass when
|
|
224
|
+
carried into a new revision: every added field is optional in the type and `nullable` in the
|
|
225
|
+
schema, and a count the model should respect lives in the `description` and is clamped in code,
|
|
226
|
+
never as `minItems`/`maxItems`. The descriptions are what the planning model reads; `gate.target`
|
|
227
|
+
is filled by code and described as such.
|
|
228
|
+
|
|
229
|
+
**A field that crosses a version skew carries no `enum`.** Users run `viable-mcp` from a moving
|
|
230
|
+
`npx` range against a separately deployed platform, so `ConnectCapabilitiesSchema.executors.items`
|
|
231
|
+
is a bare string: a newer executor kind is an unused capability on an older platform, never a
|
|
232
|
+
refused session. Apply this to what a newer connector may send an older platform and nowhere else.
|
|
272
233
|
|
|
273
234
|
## One immutable protocol tree, bound in each runtime
|
|
274
235
|
|
|
275
|
-
`connectProtocols(opts)` is the connector's whole immutable HTTP
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
`
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
236
|
+
`connectProtocols(opts)` is the connector's whole immutable HTTP tree — aliases, paths, methods,
|
|
237
|
+
contracts, parents. The platform and the SDK bind their own materializations of it; only the
|
|
238
|
+
DEPLOYMENT's parts are injected: the guard alias, the ownership gate, and the paid local-LLM gate
|
|
239
|
+
(on `session.openDelegated` alone). One base (`/connect` unless `path` says otherwise); `connect`
|
|
240
|
+
(aliases) and `connectRef` (typed references) name the same set:
|
|
241
|
+
|
|
242
|
+
| Branch | Routes |
|
|
243
|
+
|---|---|
|
|
244
|
+
| `session` | `open` POST `/session`, `openDelegated` POST `/session/delegated`, `close` POST `/session/:sessionId/close` |
|
|
245
|
+
| `op` | `pull` GET `/session/:sessionId/ops` (the long poll), `submit` POST `/session/:sessionId/ops/:opId` |
|
|
246
|
+
| `project` | `create` POST / `list` GET `/project`, `attach` POST `/project/attach`, `confirm`, `status`, `reinit`, `modify` under `/project/:id/…`, `branding.get` GET / `.save` POST `/project/:id/branding` |
|
|
247
|
+
| `story` | `status` GET `/project/:id/story/:storyId/status` |
|
|
248
|
+
| `files` | `list` GET `/project/:id/files` |
|
|
249
|
+
| `convert` | `create` POST `/convert`, `check` GET `/convert/:id/check`, `start`, `proceed`, `purge` POST `/convert/:id/…`, `status` GET `/convert/:id` |
|
|
250
|
+
| `inquiry` | `answer` POST `/project/:id/inquiry/:inquiryId` |
|
|
251
|
+
| `pipeline` | `state` GET `/pipeline/:id/:runId`, `resume` POST `/pipeline/:id/:runId/resume` |
|
|
252
|
+
|
|
253
|
+
- No socket, capability view, session read, heartbeat, conversion cancel or inference-settings
|
|
254
|
+
route exists. A project's inference modes are set by the browser through the platform's own API;
|
|
255
|
+
`ConnectProjectSettings`, `ConnectProjectLlmBody(Schema)`, `ConverterProjectLlmBody(Schema)` stay
|
|
256
|
+
here as that API's shapes.
|
|
257
|
+
- A new route is added here first (alias, declaration, `connectRef` entry), then bound in every
|
|
258
|
+
runtime that serves or calls it — a route declared on one side alone is an unexplained 404. Raw
|
|
259
|
+
aliases stay private to declaration modules; consumers receive protocol objects.
|
|
260
|
+
- NO story routes: a story is a planning card, listed, created, edited, deleted and started through
|
|
261
|
+
the planning tree the platform mounts beside the connector (`makePlanningProtocols`).
|
|
262
|
+
`ConnectProjectStatus.project` flattens the project card into a parent agent's names (`name` =
|
|
263
|
+
title, `alias` = code, the three brief bodies) plus `status` and `intrinsic`;
|
|
264
|
+
`ConnectConfirmBody` carries the three brief parts including `designSystem`.
|
|
265
|
+
- Branding routes hang under `base` (guard + ownership gate, no paid gate). The save body
|
|
266
|
+
(`ConnectProjectBrandingSaveSchema`) is a PATCH of strings with structural bounds only
|
|
267
|
+
(`CONNECT_BRANDING_*_MAX`, each equal to its platform twin); value acceptability is the
|
|
268
|
+
platform's rule on the MERGED record. The platform credit is a paid capability with its own route.
|
|
269
|
+
- Long work is read through its domain view — `ConnectProjectStatus`, `ConnectStoryStatus`,
|
|
270
|
+
`ConversionStatusView`, `ConnectPipelineState` — each composing its record, run, pending inquiry
|
|
271
|
+
and `waitingFor` reason; none exposes a generic operation id. A new long-running domain extends
|
|
272
|
+
its view and endpoint, never a parallel polling vocabulary.
|
|
307
273
|
|
|
308
274
|
## The intent-first hand-off (`./intent`)
|
|
309
275
|
|
|
310
|
-
A prompt typed on the PUBLIC site (
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
`landing` with `{ routeOptions: { overrides: { service } } }`,
|
|
321
|
-
|
|
322
|
-
cannot exist on the wire.
|
|
276
|
+
A prompt typed on the PUBLIC site (a static app that cannot import the platform repo) reaches the
|
|
277
|
+
platform through this subpath: the site, the API services (stash and pickup may be served by
|
|
278
|
+
different ones) and the manager web agree on every address, schema and step. Platform side:
|
|
279
|
+
`/intent-first`.
|
|
280
|
+
|
|
281
|
+
- **Two guest routes and one screen, no guard, no gate, no service.** `stash` POST
|
|
282
|
+
`/public/intent` (`{ prompt 1..INTENT_PROMPT_MAX (8192), consent: const true }` →
|
|
283
|
+
`{ ref, expiresAt }`), `pickup` POST `/public/intent/pickup` (`{ ref }`, exactly
|
|
284
|
+
`INTENT_REF_LENGTH` (24) Base58 → `{ prompt }`), `landing` (`frontend()` at `/start`, `sticky`).
|
|
285
|
+
Mount the tree OUTSIDE any guarded parent (a route inherits every ancestor's guard) and fill
|
|
286
|
+
`service` yourself (the site binds `landing` with `{ routeOptions: { overrides: { service } } }`,
|
|
287
|
+
which also makes its `url()` absolute).
|
|
323
288
|
- **The prompt never travels in a URL or a flow token.** The server keeps it `INTENT_TTL_SECONDS`
|
|
324
|
-
(120) under the unguessable `ref
|
|
325
|
-
crosses
|
|
326
|
-
carries the reference or nothing.
|
|
289
|
+
(120) under the unguessable `ref` and collects it with one `GETDEL`; only `?ref=`
|
|
290
|
+
crosses (the flow payload is unescaped CSV and the token unsigned).
|
|
327
291
|
- **`intentFlow` is walked on both sides and never becomes the live flow model.** `compose`
|
|
328
|
-
(initial, the site) →`handoff`→ `land` (initial,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
deployment ever restricts them globally.
|
|
338
|
-
- **The browser draft is IndexedDB, per origin.** `IntentDraft` (`id`, `ref`, `prompt`, `expiresAt`) is
|
|
339
|
-
what the platform's own browser keeps between pickup and the decision (`INTENT_DRAFT_TTL_MS`, 24 h,
|
|
340
|
-
checked on read — a client resource has no TTL). The public site cannot write it; that is the whole
|
|
341
|
-
reason for the server detour.
|
|
342
|
-
|
|
343
|
-
Tests: `tests/intent.spec.ts` (the four declarations and their absence of guards, the schema
|
|
344
|
-
accept/reject cases including crafted references, the flow walk both sides and what `suspendFlow`
|
|
345
|
-
would persist, the error statuses).
|
|
292
|
+
(initial, the site) →`handoff`→ `land` (initial, the landing) →`review`→ `review` (`HOME`), plus
|
|
293
|
+
the EXPLICIT `land` →`sign-in`→ `sign-in` (`DISPATCHER`) →`next`→ `review`. A signed-out visitor
|
|
294
|
+
is parked with `suspendFlow` at `sign-in`. The parameter is `?ref=` — never `?flow=` (`web-flow`
|
|
295
|
+
parses it on every page load) and never `?intent=` (the login surrogate window owns it).
|
|
296
|
+
- **CORS is not declared here**: the routes ride the global `origin: '*'` (no credentials);
|
|
297
|
+
`makeIntentProtocols` carries a `TODO(cors)` naming the origins to keep if that is restricted.
|
|
298
|
+
- **The browser draft is IndexedDB, per origin.** `IntentDraft` (`id`, `ref`, `prompt`,
|
|
299
|
+
`expiresAt`) is kept by the platform's browser between pickup and decision
|
|
300
|
+
(`INTENT_DRAFT_TTL_MS`, 24 h, checked on read).
|
|
346
301
|
|
|
347
302
|
## Slot metadata keys: optional means omitted
|
|
348
303
|
|
|
349
|
-
`SlotMetadata` is what the platform signs and pushes into a slot
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
`
|
|
355
|
-
build reads `''` as "none".
|
|
304
|
+
`SlotMetadata` is what the platform signs and pushes into a slot; the publisher refuses the WHOLE
|
|
305
|
+
configuration with a 401 when the body carries a key its schema does not know. A key added after
|
|
306
|
+
slots exist is therefore OPTIONAL in the type and OMITTED from a push while unset:
|
|
307
|
+
`brandingGoogleTag` (in `metadataConfigs`), `cspSources` (in `SlotListMetadata`). The build
|
|
308
|
+
ENVIRONMENT differs: `brandingEnv` always emits every `BRANDING_ENV_KEYS` entry,
|
|
309
|
+
`BRANDING_GOOGLE_TAG` as `''` when unset.
|
|
356
310
|
|
|
357
|
-
`metadataConfigs` / `metadataLists` / `metadataSecrets` enumerate what an owner STORES
|
|
358
|
-
`project-config` / `project-secret` row per key
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
production build writes), so the tag and the hosts it needs cannot disagree.
|
|
311
|
+
`metadataConfigs` / `metadataLists` / `metadataSecrets` enumerate what an owner STORES (one
|
|
312
|
+
`project-config` / `project-secret` row per key). `cspSources` is not an owner setting, so it is
|
|
313
|
+
on the type only; the Google tag's CSP hosts never travel in it — the publisher derives them from
|
|
314
|
+
`brandingGoogleTag`. Push rules: `/slot-config`.
|
|
362
315
|
|
|
363
|
-
## Blueprint types and
|
|
316
|
+
## Blueprint types, cases, skill and persona names
|
|
364
317
|
|
|
365
318
|
`src/blueprint/` holds only types and constants. A `Blueprint` has five REQUIRED build layers
|
|
366
319
|
(`technology`, `stack`, `template`, `createApp`, `packages`) and one OPTIONAL `experience` layer —
|
|
367
|
-
what the product
|
|
368
|
-
through a helper
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
`
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
`ViablePersona` (who a helper's model is told it is). They are here because a `Blueprint`'s
|
|
385
|
-
`stack.skills` / `packages.skills` / `personaSkills` name them. The catalogue behind them — every
|
|
386
|
-
skill body (`VIABLE_SKILLS`) and weight (`SKILL_ORDER`), `FRAMEWORK_OWNED_SKILLS` /
|
|
387
|
-
`skillsForBlueprint`, each persona's prompt policy (`VIABLE_PERSONAS`), the design house style and
|
|
388
|
-
the landing-gate teaching — lives in `@owlmeans/viable` `src/skills/` (its `personas-and-skills`
|
|
389
|
-
skill). Adding a member here is half a change: `SKILL_ORDER` and `VIABLE_PERSONAS` are total records
|
|
390
|
-
and fail to compile until the new member has a weight or a policy, but `VIABLE_SKILLS` is a list,
|
|
391
|
-
and the prompt service skips an alias it cannot resolve — a missing body is a rule no model ever
|
|
392
|
-
receives.
|
|
320
|
+
what the product does for its users, changing no dependency and no coder prompt. Read `experience`
|
|
321
|
+
only through a total helper: `landingGatePreferenceOf` (`LandingGatePreference.Allow` for no layer,
|
|
322
|
+
no key or an unknown value) and `tenancyOf`. An execution carries a `BlueprintRef` — `id`,
|
|
323
|
+
optional `case`, optional `tenancy`, an override patch — never a resolved blueprint.
|
|
324
|
+
|
|
325
|
+
`BlueprintCase` (`web`, `scalable`, `ai-pipeline`, `ai-agent`, `game`) and `GameKind` are what
|
|
326
|
+
`fields.blueprintCase` / `fields.gameKind` carry. What a case MEANS (`BLUEPRINT_CASES`,
|
|
327
|
+
`applyBlueprintCase`, `applyBlueprintPatch` / `freezeBlueprint`, `joinPersonaSkills`, the
|
|
328
|
+
classification schema) lives in `@owlmeans/viable` (`/blueprints`, `/blueprint-cases`); that table
|
|
329
|
+
is `Record<BlueprintCase, …>`, so a new member fails to compile there until it has a row.
|
|
330
|
+
|
|
331
|
+
`src/skills/` holds two enums: `ViableSkill` (prompt-skill aliases) and `ViablePersona`, named by
|
|
332
|
+
a `Blueprint`'s `stack.skills` / `packages.skills` / `personaSkills`. The catalogue (`VIABLE_SKILLS`,
|
|
333
|
+
`SKILL_ORDER`, `FRAMEWORK_OWNED_SKILLS` / `skillsForBlueprint`, `VIABLE_PERSONAS`) lives in
|
|
334
|
+
`@owlmeans/viable` (`/personas-and-skills`). A new member is half a change: `SKILL_ORDER` and
|
|
335
|
+
`VIABLE_PERSONAS` fail to compile without it, but `VIABLE_SKILLS` is a list and the prompt service
|
|
336
|
+
skips an unresolved alias — a missing body is a rule no model receives.
|
|
393
337
|
|
|
394
338
|
## Closed sets that mean something
|
|
395
339
|
|
|
396
340
|
- **`ProjectArea`** — guest `/`, user `/frontoffice`, admin `/admin`, operator `/backoffice`. A
|
|
397
|
-
|
|
398
|
-
`AREA_ACCESS` and `AREA_TIER` are total over it.
|
|
341
|
+
product's own roles map onto `user` or `operator`; there is no fifth area. `AREA_PATHS`,
|
|
342
|
+
`AREA_ACCESS` and `AREA_TIER` are total over it (`/target-areas`).
|
|
343
|
+
- **`PermissionDefault`** — `none` / `user` / `member` / `owner`, the default class of a generated
|
|
344
|
+
permission (the IAM's `IamDefaultClass` values, `/iam`): evaluated when claims are issued, never
|
|
345
|
+
copied onto rows; `member` / `owner` are the organization being acted in, so they apply only to
|
|
346
|
+
an organization-bound permission.
|
|
347
|
+
- **`AccessBlock`** — a model writes `permissions` (`<resource>--<action>`, optionally
|
|
348
|
+
`@<routeParam>`), `level` and `defaultEnabledPermissions?` (the subset safe for every signed-in
|
|
349
|
+
user). CODE writes `defaults` (`PermissionDefault` per permission name, keyed with the `@` selector
|
|
350
|
+
stripped) and `entityScoped` from the tenancy and the area, and empties
|
|
351
|
+
`defaultEnabledPermissions` in a tenanted area. `AccessBlockSchema` declares only the model keys
|
|
352
|
+
and is pinned byte for byte (`tests/access-schema.spec.ts`); widening it changes every access
|
|
353
|
+
prompt. Per-record kinds and their three names (`<kind>--view` / `--modify` / `--create`) are
|
|
354
|
+
declared by the library's access policy (`/target-tenancy`).
|
|
399
355
|
- **`TargetLayout` + `TARGET_LAYOUTS`** — the integrity manifest is PER LAYOUT. A volume never
|
|
400
|
-
migrates, so a
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
- **`SubProject`** — a ROLE vocabulary mapped per layout, never joined onto a path.
|
|
404
|
-
generations arrive on the wire
|
|
405
|
-
|
|
406
|
-
- **`ConversionStage`** — advanced only through `stageAfter` / `canEnter` / `decisionFor
|
|
407
|
-
transition computed at a call site
|
|
408
|
-
- **`ModerationCategory`** — a wire contract with eight languages of wording behind it
|
|
356
|
+
migrates, so a legacy-layout (v1) project stays on its tree and must verify **clean**;
|
|
357
|
+
`TARGET_INTEGRITY_FILES` is the union over layouts, so a caller has both probes before it knows
|
|
358
|
+
its tree, and an absent layout reads as the CURRENT one (`/workload-integrity`).
|
|
359
|
+
- **`SubProject`** — a ROLE vocabulary mapped per layout (`ROLE_DIRS`), never joined onto a path.
|
|
360
|
+
Both generations arrive on the wire, so the enum stays total over what any live agent sends, and
|
|
361
|
+
no role but `Common` resolves to the `common` directory.
|
|
362
|
+
- **`ConversionStage`** — advanced only through `stageAfter` / `canEnter` / `decisionFor`; a
|
|
363
|
+
transition computed at a call site re-enters a stage already paid for.
|
|
364
|
+
- **`ModerationCategory`** — a wire contract with eight languages of wording behind it; a fifth
|
|
409
365
|
shape is PHRASED into one of the four, never added.
|
|
410
366
|
|
|
411
|
-
## The conversion vocabulary is shared
|
|
367
|
+
## The conversion vocabulary is shared by three executors
|
|
412
368
|
|
|
413
|
-
The census, listing and relocation commands
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
keep what a volume it owns must not lose, the SDK's keeps what a DEVELOPER owns (`.viable`, the two
|
|
421
|
-
`.env` files).
|
|
369
|
+
The census, listing and relocation commands have three implementations — the SDK's local executor,
|
|
370
|
+
the publisher's file helper, the library's in-process helper — so whatever they could disagree
|
|
371
|
+
about is a constant HERE: `CENSUS_SKIP_DIRS` (`node_modules`, `.git` — never `dist`/`build`/`.next`,
|
|
372
|
+
which an origin may keep sources in), `SOURCE_LIST_EXCLUSIONS` (the metadata directories plus
|
|
373
|
+
`CONVERTED_ORIGIN_DIR`), `BINARY_PROBE_BYTES`, `CENSUS_MAX_HEAD_BYTES`, `RELOCATE_ALWAYS_KEEP`. The
|
|
374
|
+
one allowed difference is stated: the platform's two keep what a volume it owns must not lose, the
|
|
375
|
+
SDK's keeps what a DEVELOPER owns (`.viable`, the two `.env` files).
|
|
422
376
|
|
|
423
|
-
`docs/conversion/` is
|
|
424
|
-
|
|
425
|
-
|
|
377
|
+
`docs/conversion/` is addressed only through the path builders (`CONVERSION_*_FILE`,
|
|
378
|
+
`conversionStoryDoc`, `conversionSeedDoc`) — the purge reads them back, and a path spelled at a
|
|
379
|
+
call site is a file the purge leaves behind.
|
|
426
380
|
|
|
427
|
-
## The inquiry vocabulary is a deliberate COPY
|
|
381
|
+
## The inquiry vocabulary is a deliberate COPY; the ceiling is not
|
|
428
382
|
|
|
429
383
|
`ConnectInquiryKind` / `InquiryPayload` / `InquiryAnswerPayload` mirror `@owlmeans/llm-common`'s
|
|
430
|
-
`Inquiry` family,
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
`
|
|
437
|
-
|
|
438
|
-
`docs/conversion/inquiries.md` — equals its twin of the same name there. Two ceilings for one value means the layer with the larger one
|
|
439
|
-
truncates silently at the smaller, and the caller records an assumption about an answer the person
|
|
440
|
-
actually gave. Never introduce a local cap; import the one that exists.
|
|
384
|
+
`Inquiry` family under other names, so both import into one file and the API services stay free of
|
|
385
|
+
the model runtime. Values are byte-identical (the platform's mapper is a widening; a test pins it),
|
|
386
|
+
as `ModelTask` is with `DelegatedTask`.
|
|
387
|
+
|
|
388
|
+
The CEILING exists once: `CONNECT_INQUIRY_MAX_TEXT` equals `DEFAULT_INQUIRY_ANSWER_CHARS`, and
|
|
389
|
+
`./convert`'s `INQUIRY_STATE_TEXT_CHARS` (what a pipeline STATE keeps of an answer; the rest goes
|
|
390
|
+
to `docs/conversion/inquiries.md`) equals its namesake there. Two ceilings for one value truncate
|
|
391
|
+
silently at the smaller; never introduce a local cap.
|
|
441
392
|
|
|
442
393
|
## Errors: declared here, phrased where they are read
|
|
443
394
|
|
|
444
395
|
`ConnectError` and its family (`ConnectSessionNotFound`, `ConnectSessionGone`, `ConnectOpTimeout`,
|
|
445
396
|
`ConnectOpRefused`, `LocalSlotUnsupported`, `ConnectOpUnknown`, `ConnectOutOfCredits`,
|
|
446
|
-
`ConnectConsentRequired`) are `ResilientError` classes with
|
|
447
|
-
refusals a connector
|
|
448
|
-
|
|
449
|
-
`
|
|
450
|
-
|
|
451
|
-
`
|
|
452
|
-
`
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
connector returns.
|
|
397
|
+
`ConnectConsentRequired`) are `ResilientError` classes with `viable-connect:` markers. The two
|
|
398
|
+
refusals a connector phrases for a person pack their fields into the message (only `type` and
|
|
399
|
+
`message` survive a marshal), are built with `static encode(...)` and rebuilt in
|
|
400
|
+
`finalizeUnmarshal()`:
|
|
401
|
+
|
|
402
|
+
- `ConnectOutOfCredits` — `out-of-credits:<gate>:<requiredUsd>:<balanceUsd>:<encodeURIComponent(topUpUrl)>`;
|
|
403
|
+
- `ConnectConsentRequired` — `consent-required:<gate>:<deadline epoch ms | 0>:<encodeURIComponent(consentUrl)>`
|
|
404
|
+
(`0` = unknown; epoch ms and the URL last because ISO dates and URLs contain colons). It is the
|
|
405
|
+
connector's face of the EU spend consent (`PerformanceConsentRequired` on the web, `/payment`):
|
|
406
|
+
only a PERSON gives it, in the browser at `consentUrl`.
|
|
407
|
+
|
|
408
|
+
`ConnectSessionGone` is registered FATAL on the agent side: the step fails as an OUTCOME, so the
|
|
409
|
+
run row records where it stopped and `pipeline.resume` picks it up when a connector returns.
|
|
460
410
|
|
|
461
411
|
The project and story refusals — `ProjectNotFound`, `ProjectPermissionError`,
|
|
462
|
-
`ProjectStoryNotFound`, `ProjectStoryMissconfigured`, `ProjectAgentOccupied` and
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
412
|
+
`ProjectStoryNotFound`, `ProjectStoryMissconfigured`, `ProjectAgentOccupied` and their bases
|
|
413
|
+
(`ProjectResourceError`, `ProjectStoryError`, `ProjectError`, `ProjectAgentError`) — are declared
|
|
414
|
+
here because the viable planning plugin throws them from a process-neutral package. Type names and
|
|
415
|
+
`viable-project:` markers are wire contracts. The platform re-exports the family and never
|
|
416
|
+
redeclares a base: unmarshalling is by type name with the last registration winning, so a second
|
|
417
|
+
declaration breaks `instanceof`.
|
|
468
418
|
|
|
469
|
-
Every refusal
|
|
470
|
-
|
|
419
|
+
Every refusal declares its HTTP status on its leaf class (`/error`); `tests/error-status.spec.ts`
|
|
420
|
+
refuses an exported class whose status was not decided:
|
|
471
421
|
|
|
472
422
|
| Status | Classes |
|
|
473
423
|
|---|---|
|
|
474
424
|
| 402 | `ConnectOutOfCredits` |
|
|
475
|
-
| 428 | `ConnectConsentRequired`
|
|
425
|
+
| 428 | `ConnectConsentRequired` |
|
|
476
426
|
| 404 | `ProjectNotFound`, `ProjectStoryNotFound`, `ConnectSessionNotFound`, `ConnectOpUnknown` |
|
|
477
|
-
| 409 | `ProjectAgentOccupied`, `ProjectStoryMissconfigured`, `ConnectSessionGone` (no connector attached), `LocalSlotUnsupported`
|
|
478
|
-
| 422 | `ConnectOpRefused`
|
|
427
|
+
| 409 | `ProjectAgentOccupied`, `ProjectStoryMissconfigured`, `ConnectSessionGone` (no connector attached), `LocalSlotUnsupported` |
|
|
428
|
+
| 422 | `ConnectOpRefused` |
|
|
479
429
|
| none (500) | the bases, `ConnectOpTimeout` (a peer's fault), `ProjectPermissionError` (never thrown; a permission refusal extends `AuthForbidden`) |
|
|
480
430
|
|
|
481
|
-
The platform's other refusals
|
|
482
|
-
the product repo,
|
|
483
|
-
|
|
484
|
-
which is what stops one refusal being phrased two ways. When a marker changes, both readers change
|
|
485
|
-
with it.
|
|
431
|
+
The platform's other refusals (conversion, moderation, reserved names, integrity) are declared in
|
|
432
|
+
the product repo, so a consumer matches them by MARKER: `@owlmeans/viable-sdk`'s `REFUSALS` map and
|
|
433
|
+
the manager's `useErrorPhrase` read the same substrings. A marker change changes both readers.
|
|
486
434
|
|
|
487
435
|
## Tests
|
|
488
436
|
|
|
489
|
-
`bun test ./tests` — offline.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
`
|
|
497
|
-
|
|
498
|
-
`connect-entrypoints.spec.ts`
|
|
499
|
-
the
|
|
500
|
-
`
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
gate; capabilities accepting an executor kind this platform has never heard of; the answer and
|
|
505
|
-
resume bodies), `design.spec.ts` (the design aggregate, staleness ranking, and the design schema
|
|
506
|
-
refusing an undeclared field or an area outside the closed set, and `userStoryOfDesign`),
|
|
507
|
-
`error-status.spec.ts` (every exported refusal's declared status, and its class and status after a
|
|
508
|
-
marshal round trip), `connect-errors.spec.ts` (the packed fields of `ConnectOutOfCredits` and
|
|
509
|
-
`ConnectConsentRequired` fresh and after a round trip, an unknown deadline as `0`).
|
|
437
|
+
`bun test ./tests` — offline.
|
|
438
|
+
|
|
439
|
+
| File | Pins |
|
|
440
|
+
|---|---|
|
|
441
|
+
| `planning.spec.ts` | the two flows and transition tables, type declarations, slots, code policies, reserved types outside `cardTypes`, field schemas (null optionals, refused strays and closed-set values), landing fields and sentence, the tenancy decision, card helpers, the `follows` anchor, refusal type names after a marshal |
|
|
442
|
+
| `tenancy.spec.ts` | `NO_TENANCY` frozen, `tenancyOf` defaults and the literal-`true` rule, `tenantedArea` |
|
|
443
|
+
| `access-schema.spec.ts` | the model-facing access schema: model keys only, byte-identical |
|
|
444
|
+
| `scaffold.spec.ts` | old- and new-shape plans passing the schema and slot, `null` optionals, no `minItems` |
|
|
445
|
+
| `blueprint.spec.ts` · `branding.spec.ts` | `landingGatePreferenceOf` defaults · the build env and metadata vocabulary |
|
|
446
|
+
| `connect-entrypoints.spec.ts` · `connect-convert.spec.ts` | every route's method and path, aliases = `connectRef`, the paid gate on the delegated session alone, no socket or story route, branding body · conversion routes, an unknown executor kind accepted |
|
|
447
|
+
| `convert.spec.ts` | the three structural walks over the barrel, nullable enums under Ajv, census classifiers, stage transitions |
|
|
448
|
+
| `design.spec.ts` | the design aggregate, staleness ranking, schema refusals, `userStoryOfDesign` |
|
|
449
|
+
| `error-status.spec.ts` · `connect-errors.spec.ts` | declared statuses through a marshal · packed refusal fields fresh and after a round trip |
|
|
450
|
+
| `intent.spec.ts` | the four declarations without guards, schema cases incl. crafted refs, the flow walk and `suspendFlow` payload, error statuses |
|
|
451
|
+
| `presentation.spec.ts` | the agent-output classifier (`/agent-presentation`) |
|
|
510
452
|
|
|
511
453
|
## Depends On
|
|
512
454
|
|
|
513
|
-
- `@owlmeans/planning` — the workcard contracts the planning module declares
|
|
455
|
+
- `@owlmeans/planning` — the workcard contracts the planning module declares over
|
|
514
456
|
- `@owlmeans/resource` — the `ResourceError` base of the project refusals
|
|
515
457
|
- `@owlmeans/entrypoint`, `@owlmeans/route` — the entrypoint declarations
|
|
516
458
|
- `@owlmeans/error` — the `Connect*` error family
|
|
517
459
|
- `@owlmeans/llm-common` — `ExecutionEffort`/`ExecutionLevel`, `LlmPurpose`, the spectator
|
|
518
460
|
contracts, and the inquiry ceilings this package's copies are pinned to
|
|
519
|
-
- `@owlmeans/agent-common` — the run and pipeline contracts
|
|
520
|
-
- `@owlmeans/flow`, `@owlmeans/auth`, `@owlmeans/context` — `./intent` only: `ShallowFlow
|
|
521
|
-
|
|
461
|
+
- `@owlmeans/agent-common` — the run and pipeline contracts (re-exported `AgentRunStatus`)
|
|
462
|
+
- `@owlmeans/flow`, `@owlmeans/auth`, `@owlmeans/context` — `./intent` only: `ShallowFlow`, the
|
|
463
|
+
`DISPATCHER` and `HOME` aliases its flow steps address
|
|
522
464
|
|
|
523
465
|
## Related
|
|
524
466
|
|
|
525
|
-
- [[viable-sdk]] — the connector SDK written
|
|
526
|
-
|
|
527
|
-
- [[flow]] · [[client-flow]] — the model `intentFlow` is walked with, and `suspendFlow
|
|
528
|
-
- [[inquiry]] — the primitive `InquiryPayload` mirrors
|
|
529
|
-
- [[
|
|
530
|
-
-
|
|
531
|
-
-
|
|
532
|
-
|
|
533
|
-
(`target-areas`, `scaffolding`, `workload-integrity`, `viable-metadata`, `converter`). This skill
|
|
534
|
-
owns the CONTRACT: how a name is declared, and what refuses it.
|
|
467
|
+
- [[viable-sdk]] · [[viable-mcp]] — the connector SDK written against these contracts, and its npx
|
|
468
|
+
stdio server
|
|
469
|
+
- [[flow]] · [[client-flow]] — the model `intentFlow` is walked with, and `suspendFlow`
|
|
470
|
+
- [[inquiry]] · [[llm-common]] — the primitive `InquiryPayload` mirrors; the model runtime's contracts
|
|
471
|
+
- [[planning]] — the workcard model the planning module builds on
|
|
472
|
+
- DOMAIN meaning lives downstream (`target-areas`, `target-tenancy`, `scaffolding`,
|
|
473
|
+
`workload-integrity`, `viable-metadata`, `converter`); this skill owns the CONTRACT — how a name
|
|
474
|
+
is declared and what refuses it.
|