@company-semantics/contracts 95.0.0 → 97.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "95.0.0",
3
+ "version": "97.0.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -52,6 +52,14 @@ only the binding KIND.
52
52
  below). An action on one thing names it by its id, never by a URL — the
53
53
  binding owns the route. None reads a live availability condition yet; adding
54
54
  one is an append.
55
+ - **The execution-actions wave follows `execution.open`** (PRD-01153). Four
56
+ ids act on one analysis workflow's run: `workflow.stop`, `workflow.continue`,
57
+ `workflow.retry`, `workflow.runAgain` (see
58
+ [the execution-actions wave](#the-execution-actions-wave) below). They are
59
+ offered, never projected, and only by the workflow execution offer table in
60
+ `src/workflows/`, which decides from the workflow's state, so none reads a
61
+ live availability condition. The wave adds no availability id, unavailable
62
+ reason or no-affordance reason.
55
63
  - **Reverse coverage is exactly one of two things.** Every capability a person
56
64
  can act on declares `{ affordance }` or `{ noAffordance }` with a reason from
57
65
  the closed `NO_AFFORDANCE_REASONS` (`readOnly`, `inlineControl`,
@@ -89,13 +97,28 @@ id with no arguments has no `args` key. The binding kind comes from
89
97
 
90
98
  Every argument is a non-empty string and every args object is strict.
91
99
 
100
+ ## The execution-actions wave
101
+
102
+ The four ids appended after `execution.open` (PRD-01153), in catalog order.
103
+ All four share one strict args schema, a uuid because the backend route param
104
+ it binds to is one; all four are `command`s and read no live availability
105
+ condition (`null` in `AFFORDANCE_AVAILABILITY_OF`).
106
+
107
+ | Id | Args (schema) | Binding kind |
108
+ | ------------------- | ------------------------------------------------------ | ------------ |
109
+ | `workflow.stop` | `{ workflowId }` (`WorkflowExecutionArgsSchema`, uuid) | `command` |
110
+ | `workflow.continue` | `{ workflowId }` (`WorkflowExecutionArgsSchema`, uuid) | `command` |
111
+ | `workflow.retry` | `{ workflowId }` (`WorkflowExecutionArgsSchema`, uuid) | `command` |
112
+ | `workflow.runAgain` | `{ workflowId }` (`WorkflowExecutionArgsSchema`, uuid) | `command` |
113
+
92
114
  ## Public API
93
115
 
94
116
  - `AFFORDANCE_IDS`, `AffordanceId`, `AffordanceIdSchema` (`ids.ts`).
95
117
  - `AFFORDANCE_ARGS`, `NO_ARG_AFFORDANCE_IDS`, `ReviewOpenArgsSchema`,
96
118
  `SourceSyncOpenUpdateArgsSchema`, `OrgStructureOpenReviewArgsSchema`,
97
119
  `ChatOpenArgsSchema`, `CompanyMdDocArgsSchema`, `TeamOpenArgsSchema`,
98
- `ExecutionOpenArgsSchema`, `AffordanceArgs` (`args.ts`).
120
+ `ExecutionOpenArgsSchema`, `WorkflowExecutionArgsSchema`, `AffordanceArgs`
121
+ (`args.ts`).
99
122
  - `AFFORDANCE_BINDING_KINDS`, `AFFORDANCE_AVAILABILITY_IDS`,
100
123
  `AffordanceAvailabilityIdSchema`, `AFFORDANCE_UNAVAILABLE_REASONS`,
101
124
  `AffordanceAvailabilitySchema` and their types (`availability.ts`).
@@ -21,6 +21,12 @@ Locks the affordance vocabulary and the shape of an affordance instance.
21
21
  no policy field, the error envelope and billing refusal part carry
22
22
  affordances only when non-empty, and every new name is reachable from the
23
23
  package root.
24
+ - `workflow-affordances.test.ts` — pins the four execution ids (PRD-01153:
25
+ `workflow.stop`, `workflow.continue`, `workflow.retry`, `workflow.runAgain`)
26
+ appended after `execution.open`; proves each is an offered (never projected)
27
+ `command` with null availability that takes the shared
28
+ `WorkflowExecutionArgsSchema` (a uuid `workflowId`, strict), and that the wave
29
+ adds no availability id, unavailable reason or no-affordance reason.
24
30
 
25
31
  ## Invariants
26
32
 
@@ -32,7 +32,7 @@ const schemaMatchesType: Equals<
32
32
  > = true;
33
33
 
34
34
  describe("affordance vocabulary", () => {
35
- it("names the nineteen affordances, in order", () => {
35
+ it("names the twenty-three affordances, in order", () => {
36
36
  expect(AFFORDANCE_IDS).toEqual([
37
37
  "sourceSync.findFiles",
38
38
  "integrations.connectGoogleDrive",
@@ -53,6 +53,10 @@ describe("affordance vocabulary", () => {
53
53
  "companyMd.openHistory",
54
54
  "team.open",
55
55
  "execution.open",
56
+ "workflow.stop",
57
+ "workflow.continue",
58
+ "workflow.retry",
59
+ "workflow.runAgain",
56
60
  ]);
57
61
  expect(schemaMatchesType).toBe(true);
58
62
  });
@@ -252,6 +256,7 @@ describe("affordance vocabulary", () => {
252
256
  "OfferedAffordanceSchema",
253
257
  "PROJECTED_AFFORDANCE_IDS",
254
258
  "ReviewOpenArgsSchema",
259
+ "WorkflowExecutionArgsSchema",
255
260
  "sameAffordances",
256
261
  ] as const) {
257
262
  expect((root as Record<string, unknown>)[name], name).toBeDefined();
@@ -47,8 +47,8 @@ const TARGET_ARG = {
47
47
 
48
48
  describe("affordance catalog wave", () => {
49
49
  it("appends the twelve burn-down ids after the original seven, in order", () => {
50
- expect(AFFORDANCE_IDS.slice(7)).toEqual(Object.keys(WAVE));
51
- expect(AFFORDANCE_IDS).toHaveLength(19);
50
+ expect(AFFORDANCE_IDS.slice(7, 19)).toEqual(Object.keys(WAVE));
51
+ expect(AFFORDANCE_IDS).toHaveLength(23);
52
52
  });
53
53
 
54
54
  it("every new id has the locked binding kind", () => {
@@ -0,0 +1,98 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { z } from "zod";
3
+
4
+ import {
5
+ AFFORDANCE_ARGS,
6
+ AFFORDANCE_AVAILABILITY_IDS,
7
+ AFFORDANCE_AVAILABILITY_OF,
8
+ AFFORDANCE_BINDING_KIND_OF,
9
+ AFFORDANCE_IDS,
10
+ AFFORDANCE_UNAVAILABLE_REASONS,
11
+ NO_AFFORDANCE_REASONS,
12
+ NO_ARG_AFFORDANCE_IDS,
13
+ OfferedAffordanceSchema,
14
+ PROJECTED_AFFORDANCE_IDS,
15
+ WorkflowExecutionArgsSchema,
16
+ } from "../index";
17
+
18
+ // The execution-actions wave (PRD-01153): exactly these ids, appended after
19
+ // execution.open, all bound to a backend command.
20
+ const EXECUTION_IDS = [
21
+ "workflow.stop",
22
+ "workflow.continue",
23
+ "workflow.retry",
24
+ "workflow.runAgain",
25
+ ] as const;
26
+
27
+ const WORKFLOW_ID = "3f1c2a9e-6b7d-4e8f-9a0b-1c2d3e4f5a6b";
28
+
29
+ describe("workflow execution affordance ids", () => {
30
+ it("appends the four execution ids after execution.open, in order", () => {
31
+ expect(AFFORDANCE_IDS.indexOf("execution.open")).toBe(18);
32
+ expect(AFFORDANCE_IDS.slice(19)).toEqual([...EXECUTION_IDS]);
33
+ });
34
+
35
+ it("every execution id is a command that reads no live availability", () => {
36
+ for (const id of EXECUTION_IDS) {
37
+ expect(AFFORDANCE_BINDING_KIND_OF[id], id).toBe("command");
38
+ expect(AFFORDANCE_AVAILABILITY_OF[id], id).toBeNull();
39
+ }
40
+ });
41
+
42
+ it("every execution id takes the shared { workflowId } args", () => {
43
+ for (const id of EXECUTION_IDS) {
44
+ expect(AFFORDANCE_ARGS[id], id).toBe(WorkflowExecutionArgsSchema);
45
+ expect(NO_ARG_AFFORDANCE_IDS as readonly string[]).not.toContain(id);
46
+ }
47
+ });
48
+
49
+ // An instance must carry a uuid workflowId and nothing else.
50
+ it("workflow execution affordances require a workflowId", () => {
51
+ for (const id of EXECUTION_IDS) {
52
+ expect(
53
+ OfferedAffordanceSchema.safeParse({
54
+ id,
55
+ args: { workflowId: WORKFLOW_ID },
56
+ }).success,
57
+ id,
58
+ ).toBe(true);
59
+ expect(OfferedAffordanceSchema.safeParse({ id }).success, id).toBe(false);
60
+ expect(
61
+ OfferedAffordanceSchema.safeParse({ id, args: {} }).success,
62
+ id,
63
+ ).toBe(false);
64
+ expect(
65
+ OfferedAffordanceSchema.safeParse({
66
+ id,
67
+ args: { workflowId: "wf-1" },
68
+ }).success,
69
+ id,
70
+ ).toBe(false);
71
+ expect(
72
+ OfferedAffordanceSchema.safeParse({
73
+ id,
74
+ args: { workflowId: WORKFLOW_ID, chatId: "c1" },
75
+ }).success,
76
+ id,
77
+ ).toBe(false);
78
+ }
79
+ });
80
+
81
+ it("execution ids are offered, never projected", () => {
82
+ for (const id of EXECUTION_IDS) {
83
+ expect(PROJECTED_AFFORDANCE_IDS as readonly string[]).not.toContain(id);
84
+ }
85
+ });
86
+
87
+ it("adds no availability id, unavailable reason or no-affordance reason", () => {
88
+ expect(AFFORDANCE_AVAILABILITY_IDS).toHaveLength(4);
89
+ expect(AFFORDANCE_UNAVAILABLE_REASONS).toHaveLength(4);
90
+ expect(NO_AFFORDANCE_REASONS).toHaveLength(5);
91
+ });
92
+
93
+ it("the args schema converts to JSON Schema", () => {
94
+ expect(() =>
95
+ z.toJSONSchema(WorkflowExecutionArgsSchema, { io: "input" }),
96
+ ).not.toThrow();
97
+ });
98
+ });
@@ -22,6 +22,7 @@ import {
22
22
  ReviewOpenArgsSchema,
23
23
  SourceSyncOpenUpdateArgsSchema,
24
24
  TeamOpenArgsSchema,
25
+ WorkflowExecutionArgsSchema,
25
26
  type AffordanceArgs,
26
27
  } from "./args";
27
28
  import type {
@@ -77,6 +78,10 @@ const OFFERED_MEMBERS = [
77
78
  withArgs("companyMd.openHistory", CompanyMdDocArgsSchema),
78
79
  withArgs("team.open", TeamOpenArgsSchema),
79
80
  withArgs("execution.open", ExecutionOpenArgsSchema),
81
+ withArgs("workflow.stop", WorkflowExecutionArgsSchema),
82
+ withArgs("workflow.continue", WorkflowExecutionArgsSchema),
83
+ withArgs("workflow.retry", WorkflowExecutionArgsSchema),
84
+ withArgs("workflow.runAgain", WorkflowExecutionArgsSchema),
80
85
  ] as const;
81
86
 
82
87
  /** Every affordance instance, validated per id. */
@@ -112,6 +117,10 @@ export const AFFORDANCE_BINDING_KIND_OF = {
112
117
  "companyMd.openHistory": "navigate",
113
118
  "team.open": "navigate",
114
119
  "execution.open": "navigate",
120
+ "workflow.stop": "command",
121
+ "workflow.continue": "command",
122
+ "workflow.retry": "command",
123
+ "workflow.runAgain": "command",
115
124
  } as const satisfies Record<AffordanceId, AffordanceBindingKind>;
116
125
 
117
126
  /** The live condition each affordance's availability is read from, if any. */
@@ -140,6 +149,12 @@ export const AFFORDANCE_AVAILABILITY_OF = {
140
149
  "companyMd.openHistory": null,
141
150
  "team.open": null,
142
151
  "execution.open": null,
152
+ // The execution offer table (executionAffordancesFor in ../workflows)
153
+ // already decides from the workflow's state, so there is nothing to read.
154
+ "workflow.stop": null,
155
+ "workflow.continue": null,
156
+ "workflow.retry": null,
157
+ "workflow.runAgain": null,
143
158
  } as const satisfies Record<AffordanceId, AffordanceAvailabilityId | null>;
144
159
 
145
160
  /**
@@ -48,6 +48,15 @@ export const ExecutionOpenArgsSchema = z
48
48
  .object({ executionId: z.string().min(1) })
49
49
  .strict();
50
50
 
51
+ /**
52
+ * `workflow.stop` / `workflow.continue` / `workflow.retry` /
53
+ * `workflow.runAgain`: which analysis workflow. A uuid, because the backend
54
+ * route param it binds to is one.
55
+ */
56
+ export const WorkflowExecutionArgsSchema = z
57
+ .object({ workflowId: z.string().uuid() })
58
+ .strict();
59
+
51
60
  /**
52
61
  * Argument schemas, ONLY for ids that take arguments. An id that takes none is
53
62
  * listed in NO_ARG_AFFORDANCE_IDS and its instance OMITS `args` entirely: there
@@ -65,6 +74,10 @@ export const AFFORDANCE_ARGS = {
65
74
  "companyMd.openHistory": CompanyMdDocArgsSchema,
66
75
  "team.open": TeamOpenArgsSchema,
67
76
  "execution.open": ExecutionOpenArgsSchema,
77
+ "workflow.stop": WorkflowExecutionArgsSchema,
78
+ "workflow.continue": WorkflowExecutionArgsSchema,
79
+ "workflow.retry": WorkflowExecutionArgsSchema,
80
+ "workflow.runAgain": WorkflowExecutionArgsSchema,
68
81
  } as const;
69
82
 
70
83
  /** The ids whose instances carry no `args` key. */
@@ -66,6 +66,20 @@ export const AFFORDANCE_IDS = [
66
66
  "team.open",
67
67
  /** Open one execution's detail (the target of an execution confirmation). */
68
68
  "execution.open",
69
+ // ---------------------------------------------------------------------------
70
+ // Execution actions (PRD-01153): what a person may do to one analysis
71
+ // workflow's run. Offered only by executionAffordancesFor (../workflows),
72
+ // from the workflow's state and its type's execution support. Appended,
73
+ // never inserted.
74
+ // ---------------------------------------------------------------------------
75
+ /** Stop one running analysis workflow (it drains, then settles stopped). */
76
+ "workflow.stop",
77
+ /** Continue one stopped analysis workflow from where it stopped. */
78
+ "workflow.continue",
79
+ /** Retry one failed analysis workflow. */
80
+ "workflow.retry",
81
+ /** Start a fresh run of one settled analysis workflow (a relaunch). */
82
+ "workflow.runAgain",
69
83
  ] as const;
70
84
 
71
85
  export type AffordanceId = (typeof AFFORDANCE_IDS)[number];
@@ -14,6 +14,7 @@ export {
14
14
  ReviewOpenArgsSchema,
15
15
  SourceSyncOpenUpdateArgsSchema,
16
16
  TeamOpenArgsSchema,
17
+ WorkflowExecutionArgsSchema,
17
18
  } from "./args";
18
19
  export type { AffordanceArgs } from "./args";
19
20
  export {
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = 'd4db95eeefb4' as const;
3
- export const SPEC_HASH_FULL = 'd4db95eeefb4bf7a97541fcefdcb90c1804e1e39508fe0b98e6389ce001a6f16' as const;
2
+ export const SPEC_HASH = 'f571d3d78c72' as const;
3
+ export const SPEC_HASH_FULL = 'f571d3d78c720d7ad11592b2d490a723c48c539d162c41a6dd77f67bb6ad922f' as const;