@owlmeans/server-planning 0.1.18-rc.9 → 0.1.18
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 +92 -19
- package/agent-meta/manifest.json +105 -4
- package/agent-meta/skills/server-planning/SKILL.md +89 -138
- package/agent-meta/skills/server-planning/parts/auxiliary.md +54 -0
- package/agent-meta/skills/server-planning/parts/executor.md +82 -0
- package/agent-meta/skills/server-planning/parts/schemas.md +26 -0
- package/agent-meta/skills/server-planning/parts/serving.md +88 -0
- package/agent-meta/skills/server-planning/parts/stores.md +91 -0
- package/build/actions/apply-schema-request.d.ts +8 -0
- package/build/actions/apply-schema-request.d.ts.map +1 -0
- package/build/actions/apply-schema-request.js +33 -0
- package/build/actions/apply-schema-request.js.map +1 -0
- package/build/actions/commit.d.ts +3 -3
- package/build/actions/commit.d.ts.map +1 -1
- package/build/actions/commit.js +5 -4
- package/build/actions/commit.js.map +1 -1
- package/build/actions/define-schemas.d.ts +13 -0
- package/build/actions/define-schemas.d.ts.map +1 -0
- package/build/actions/define-schemas.js +28 -0
- package/build/actions/define-schemas.js.map +1 -0
- package/build/actions/execute.d.ts +11 -10
- package/build/actions/execute.d.ts.map +1 -1
- package/build/actions/execute.js +17 -27
- package/build/actions/execute.js.map +1 -1
- package/build/actions/execution/types.d.ts +34 -0
- package/build/actions/execution/types.d.ts.map +1 -0
- package/build/actions/execution/types.js +2 -0
- package/build/actions/execution/types.js.map +1 -0
- package/build/actions/execution.d.ts +4 -0
- package/build/actions/execution.d.ts.map +1 -0
- package/build/actions/execution.js +88 -0
- package/build/actions/execution.js.map +1 -0
- package/build/actions/get-card.d.ts +6 -0
- package/build/actions/get-card.d.ts.map +1 -0
- package/build/actions/get-card.js +9 -0
- package/build/actions/get-card.js.map +1 -0
- package/build/actions/get-specification.d.ts +6 -0
- package/build/actions/get-specification.d.ts.map +1 -0
- package/build/actions/get-specification.js +9 -0
- package/build/actions/get-specification.js.map +1 -0
- package/build/actions/index.d.ts +12 -3
- package/build/actions/index.d.ts.map +1 -1
- package/build/actions/index.js +11 -3
- package/build/actions/index.js.map +1 -1
- package/build/actions/links.d.ts +3 -3
- package/build/actions/links.d.ts.map +1 -1
- package/build/actions/links.js +6 -5
- package/build/actions/links.js.map +1 -1
- package/build/actions/list-card-specifications.d.ts +6 -0
- package/build/actions/list-card-specifications.d.ts.map +1 -0
- package/build/actions/list-card-specifications.js +11 -0
- package/build/actions/list-card-specifications.js.map +1 -0
- package/build/actions/list-card-transitions.d.ts +6 -0
- package/build/actions/list-card-transitions.d.ts.map +1 -0
- package/build/actions/list-card-transitions.js +11 -0
- package/build/actions/list-card-transitions.js.map +1 -0
- package/build/actions/list-cards.d.ts +6 -0
- package/build/actions/list-cards.d.ts.map +1 -0
- package/build/actions/list-cards.js +10 -0
- package/build/actions/list-cards.js.map +1 -0
- package/build/actions/list-schemas.d.ts +13 -0
- package/build/actions/list-schemas.d.ts.map +1 -0
- package/build/actions/list-schemas.js +20 -0
- package/build/actions/list-schemas.js.map +1 -0
- package/build/actions/list-specification-revisions.d.ts +6 -0
- package/build/actions/list-specification-revisions.d.ts.map +1 -0
- package/build/actions/list-specification-revisions.js +10 -0
- package/build/actions/list-specification-revisions.js.map +1 -0
- package/build/actions/resources.d.ts +5 -0
- package/build/actions/resources.d.ts.map +1 -0
- package/build/actions/resources.js +111 -0
- package/build/actions/resources.js.map +1 -0
- package/build/actions/summarize-cards.d.ts +6 -0
- package/build/actions/summarize-cards.d.ts.map +1 -0
- package/build/actions/summarize-cards.js +11 -0
- package/build/actions/summarize-cards.js.map +1 -0
- package/build/actions/transitions.d.ts +3 -3
- package/build/actions/transitions.d.ts.map +1 -1
- package/build/actions/transitions.js +4 -3
- package/build/actions/transitions.js.map +1 -1
- package/build/actions/types.d.ts +6 -0
- package/build/actions/types.d.ts.map +1 -0
- package/build/actions/types.js +2 -0
- package/build/actions/types.js.map +1 -0
- package/build/actions/watch.d.ts +2 -2
- package/build/actions/watch.d.ts.map +1 -1
- package/build/actions/watch.js +8 -6
- package/build/actions/watch.js.map +1 -1
- package/build/conformance/assert/types.d.ts +19 -0
- package/build/conformance/assert/types.d.ts.map +1 -0
- package/build/conformance/assert/types.js +2 -0
- package/build/conformance/assert/types.js.map +1 -0
- package/build/conformance/assert.d.ts +15 -0
- package/build/conformance/assert.d.ts.map +1 -0
- package/build/conformance/assert.js +52 -0
- package/build/conformance/assert.js.map +1 -0
- package/build/conformance/cases.d.ts +7 -0
- package/build/conformance/cases.d.ts.map +1 -0
- package/build/conformance/cases.js +544 -0
- package/build/conformance/cases.js.map +1 -0
- package/build/conformance/clock.d.ts +6 -0
- package/build/conformance/clock.d.ts.map +1 -0
- package/build/conformance/clock.js +12 -0
- package/build/conformance/clock.js.map +1 -0
- package/build/conformance/consts.d.ts +30 -0
- package/build/conformance/consts.d.ts.map +1 -0
- package/build/conformance/consts.js +159 -0
- package/build/conformance/consts.js.map +1 -0
- package/build/conformance/errors.d.ts +5 -0
- package/build/conformance/errors.d.ts.map +1 -0
- package/build/conformance/errors.js +8 -0
- package/build/conformance/errors.js.map +1 -0
- package/build/conformance/fixtures/types.d.ts +13 -0
- package/build/conformance/fixtures/types.d.ts.map +1 -0
- package/build/conformance/fixtures/types.js +2 -0
- package/build/conformance/fixtures/types.js.map +1 -0
- package/build/conformance/fixtures.d.ts +14 -0
- package/build/conformance/fixtures.d.ts.map +1 -0
- package/build/conformance/fixtures.js +26 -0
- package/build/conformance/fixtures.js.map +1 -0
- package/build/conformance/index.d.ts +15 -0
- package/build/conformance/index.d.ts.map +1 -0
- package/build/conformance/index.js +12 -0
- package/build/conformance/index.js.map +1 -0
- package/build/conformance/resources.d.ts +4 -0
- package/build/conformance/resources.d.ts.map +1 -0
- package/build/conformance/resources.js +228 -0
- package/build/conformance/resources.js.map +1 -0
- package/build/conformance/types.d.ts +18 -0
- package/build/conformance/types.d.ts.map +1 -0
- package/build/conformance/types.js +2 -0
- package/build/conformance/types.js.map +1 -0
- package/build/consts.d.ts +5 -0
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +5 -0
- package/build/consts.js.map +1 -1
- package/build/definitions.d.ts +18 -0
- package/build/definitions.d.ts.map +1 -0
- package/build/definitions.js +225 -0
- package/build/definitions.js.map +1 -0
- package/build/executor/changes/types.d.ts +36 -0
- package/build/executor/changes/types.d.ts.map +1 -0
- package/build/executor/changes/types.js +2 -0
- package/build/executor/changes/types.js.map +1 -0
- package/build/executor/changes.d.ts +3 -30
- package/build/executor/changes.d.ts.map +1 -1
- package/build/executor/changes.js +42 -51
- package/build/executor/changes.js.map +1 -1
- package/build/executor/code/types.d.ts +17 -0
- package/build/executor/code/types.d.ts.map +1 -0
- package/build/executor/code/types.js +2 -0
- package/build/executor/code/types.js.map +1 -0
- package/build/executor/code.d.ts +3 -14
- package/build/executor/code.d.ts.map +1 -1
- package/build/executor/code.js +50 -54
- package/build/executor/code.js.map +1 -1
- package/build/executor/consts.local.d.ts +5 -0
- package/build/executor/consts.local.d.ts.map +1 -0
- package/build/executor/consts.local.js +6 -0
- package/build/executor/consts.local.js.map +1 -0
- package/build/executor/creator/types.d.ts +33 -0
- package/build/executor/creator/types.d.ts.map +1 -0
- package/build/executor/creator/types.js +2 -0
- package/build/executor/creator/types.js.map +1 -0
- package/build/executor/creator.d.ts +11 -0
- package/build/executor/creator.d.ts.map +1 -0
- package/build/executor/creator.js +32 -0
- package/build/executor/creator.js.map +1 -0
- package/build/executor/resolve/types.d.ts +30 -0
- package/build/executor/resolve/types.d.ts.map +1 -0
- package/build/executor/resolve/types.js +2 -0
- package/build/executor/resolve/types.js.map +1 -0
- package/build/executor/resolve.d.ts +5 -34
- package/build/executor/resolve.d.ts.map +1 -1
- package/build/executor/resolve.js +169 -110
- package/build/executor/resolve.js.map +1 -1
- package/build/executor/types.d.ts +23 -0
- package/build/executor/types.d.ts.map +1 -0
- package/build/executor/types.js +2 -0
- package/build/executor/types.js.map +1 -0
- package/build/executor/validate.d.ts +6 -5
- package/build/executor/validate.d.ts.map +1 -1
- package/build/executor/validate.js +38 -27
- package/build/executor/validate.js.map +1 -1
- package/build/executor.d.ts +11 -7
- package/build/executor.d.ts.map +1 -1
- package/build/executor.js +27 -21
- package/build/executor.js.map +1 -1
- package/build/facade.d.ts +20 -2
- package/build/facade.d.ts.map +1 -1
- package/build/facade.js +144 -22
- package/build/facade.js.map +1 -1
- package/build/helper.d.ts +5 -9
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +12 -11
- package/build/helper.js.map +1 -1
- package/build/hierarchy/types.d.ts +20 -0
- package/build/hierarchy/types.d.ts.map +1 -0
- package/build/hierarchy/types.js +2 -0
- package/build/hierarchy/types.js.map +1 -0
- package/build/hierarchy.d.ts +5 -0
- package/build/hierarchy.d.ts.map +1 -0
- package/build/hierarchy.js +133 -0
- package/build/hierarchy.js.map +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/invariants/types.d.ts +6 -0
- package/build/invariants/types.d.ts.map +1 -0
- package/build/invariants/types.js +2 -0
- package/build/invariants/types.js.map +1 -0
- package/build/invariants.d.ts +5 -0
- package/build/invariants.d.ts.map +1 -0
- package/build/invariants.js +126 -0
- package/build/invariants.js.map +1 -0
- package/build/projection.d.ts +4 -10
- package/build/projection.d.ts.map +1 -1
- package/build/projection.js +7 -5
- package/build/projection.js.map +1 -1
- package/build/queue/consts.d.ts +5 -0
- package/build/queue/consts.d.ts.map +1 -0
- package/build/queue/consts.js +5 -0
- package/build/queue/consts.js.map +1 -0
- package/build/queue/declare.d.ts +5 -0
- package/build/queue/declare.d.ts.map +1 -0
- package/build/queue/declare.js +5 -0
- package/build/queue/declare.js.map +1 -0
- package/build/queue/index.d.ts +3 -18
- package/build/queue/index.d.ts.map +1 -1
- package/build/queue/index.js +2 -7
- package/build/queue/index.js.map +1 -1
- package/build/queue/types.d.ts +13 -0
- package/build/queue/types.d.ts.map +1 -0
- package/build/queue/types.js +2 -0
- package/build/queue/types.js.map +1 -0
- package/build/registry.d.ts +5 -22
- package/build/registry.d.ts.map +1 -1
- package/build/registry.js +16 -10
- package/build/registry.js.map +1 -1
- package/build/resources.d.ts +5 -0
- package/build/resources.d.ts.map +1 -0
- package/build/resources.js +287 -0
- package/build/resources.js.map +1 -0
- package/build/service.d.ts +3 -16
- package/build/service.d.ts.map +1 -1
- package/build/service.js +12 -0
- package/build/service.js.map +1 -1
- package/build/store/commits.d.ts.map +1 -1
- package/build/store/commits.js +3 -1
- package/build/store/commits.js.map +1 -1
- package/build/store/composite.d.ts +1 -0
- package/build/store/composite.d.ts.map +1 -1
- package/build/store/composite.js +7 -2
- package/build/store/composite.js.map +1 -1
- package/build/store/consts.local.d.ts +2 -0
- package/build/store/consts.local.d.ts.map +1 -0
- package/build/store/consts.local.js +2 -0
- package/build/store/consts.local.js.map +1 -0
- package/build/store/fold/types.d.ts +43 -0
- package/build/store/fold/types.d.ts.map +1 -0
- package/build/store/fold/types.js +2 -0
- package/build/store/fold/types.js.map +1 -0
- package/build/store/fold.d.ts +8 -30
- package/build/store/fold.d.ts.map +1 -1
- package/build/store/fold.js +190 -198
- package/build/store/fold.js.map +1 -1
- package/build/store/index.d.ts +1 -0
- package/build/store/index.d.ts.map +1 -1
- package/build/store/index.js.map +1 -1
- package/build/store/memory.d.ts +12 -0
- package/build/store/memory.d.ts.map +1 -1
- package/build/store/memory.js +0 -0
- package/build/store/memory.js.map +1 -1
- package/build/store/types.d.ts +15 -1
- package/build/store/types.d.ts.map +1 -1
- package/build/store/types.local.d.ts +5 -0
- package/build/store/types.local.d.ts.map +1 -0
- package/build/store/types.local.js +2 -0
- package/build/store/types.local.js.map +1 -0
- package/build/types.d.ts +126 -2
- package/build/types.d.ts.map +1 -1
- package/build/types.local.d.ts +7 -0
- package/build/types.local.d.ts.map +1 -0
- package/build/types.local.js +2 -0
- package/build/types.local.js.map +1 -0
- package/build/utils/access/types.d.ts +30 -0
- package/build/utils/access/types.d.ts.map +1 -0
- package/build/utils/access/types.js +2 -0
- package/build/utils/access/types.js.map +1 -0
- package/build/utils/access.d.ts +4 -0
- package/build/utils/access.d.ts.map +1 -0
- package/build/utils/access.js +28 -0
- package/build/utils/access.js.map +1 -0
- package/build/utils/guard/types.d.ts +23 -0
- package/build/utils/guard/types.d.ts.map +1 -0
- package/build/utils/guard/types.js +2 -0
- package/build/utils/guard/types.js.map +1 -0
- package/build/utils/guard.d.ts +7 -10
- package/build/utils/guard.d.ts.map +1 -1
- package/build/utils/guard.js +36 -32
- package/build/utils/guard.js.map +1 -1
- package/build/utils/handler/types.d.ts +26 -0
- package/build/utils/handler/types.d.ts.map +1 -0
- package/build/utils/handler/types.js +2 -0
- package/build/utils/handler/types.js.map +1 -0
- package/build/utils/handler.d.ts +6 -0
- package/build/utils/handler.d.ts.map +1 -0
- package/build/utils/handler.js +21 -0
- package/build/utils/handler.js.map +1 -0
- package/build/utils/index.d.ts +8 -0
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +3 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/request/types.d.ts +23 -0
- package/build/utils/request/types.d.ts.map +1 -0
- package/build/utils/request/types.js +2 -0
- package/build/utils/request/types.js.map +1 -0
- package/build/utils/request.d.ts +4 -0
- package/build/utils/request.d.ts.map +1 -0
- package/build/utils/request.js +27 -0
- package/build/utils/request.js.map +1 -0
- package/build/utils/scope.d.ts +17 -13
- package/build/utils/scope.d.ts.map +1 -1
- package/build/utils/scope.js +21 -35
- package/build/utils/scope.js.map +1 -1
- package/build/utils/types.d.ts +8 -0
- package/build/utils/types.d.ts.map +1 -0
- package/build/utils/types.js +2 -0
- package/build/utils/types.js.map +1 -0
- package/package.json +30 -22
- package/src/actions/apply-schema-request.ts +36 -0
- package/src/actions/commit.ts +7 -5
- package/src/actions/define-schemas.ts +34 -0
- package/src/actions/execute.ts +32 -34
- package/src/actions/execution/types.ts +34 -0
- package/src/actions/execution.ts +100 -0
- package/src/actions/get-card.ts +14 -0
- package/src/actions/get-specification.ts +14 -0
- package/src/actions/index.ts +13 -3
- package/src/actions/links.ts +8 -6
- package/src/actions/list-card-specifications.ts +15 -0
- package/src/actions/list-card-transitions.ts +15 -0
- package/src/actions/list-cards.ts +14 -0
- package/src/actions/list-schemas.ts +29 -0
- package/src/actions/list-specification-revisions.ts +15 -0
- package/src/actions/resources.ts +79 -0
- package/src/actions/summarize-cards.ts +15 -0
- package/src/actions/transitions.ts +6 -4
- package/src/actions/types.ts +5 -0
- package/src/actions/watch.ts +11 -7
- package/src/conformance/assert/types.ts +16 -0
- package/src/conformance/assert.ts +67 -0
- package/src/conformance/cases.ts +631 -0
- package/src/conformance/clock.ts +11 -0
- package/src/conformance/consts.ts +165 -0
- package/src/conformance/errors.ts +7 -0
- package/src/conformance/fixtures/types.ts +11 -0
- package/src/conformance/fixtures.ts +46 -0
- package/src/conformance/index.ts +14 -0
- package/src/conformance/resources.ts +227 -0
- package/src/conformance/types.ts +20 -0
- package/src/consts.ts +6 -0
- package/src/definitions.ts +264 -0
- package/src/executor/changes/types.ts +38 -0
- package/src/executor/changes.ts +49 -68
- package/src/executor/code/types.ts +23 -0
- package/src/executor/code.ts +62 -69
- package/src/executor/consts.local.ts +8 -0
- package/src/executor/creator/types.ts +33 -0
- package/src/executor/creator.ts +43 -0
- package/src/executor/resolve/types.ts +30 -0
- package/src/executor/resolve.ts +181 -139
- package/src/executor/types.ts +23 -0
- package/src/executor/validate.ts +40 -38
- package/src/executor.ts +33 -27
- package/src/facade.ts +132 -29
- package/src/helper.ts +12 -11
- package/src/hierarchy/types.ts +20 -0
- package/src/hierarchy.ts +113 -0
- package/src/index.ts +1 -0
- package/src/invariants/types.ts +6 -0
- package/src/invariants.ts +106 -0
- package/src/projection.ts +12 -18
- package/src/queue/consts.ts +5 -0
- package/src/queue/declare.ts +7 -0
- package/src/queue/index.ts +3 -25
- package/src/queue/types.ts +14 -0
- package/src/registry.ts +18 -36
- package/src/resources.ts +256 -0
- package/src/service.ts +16 -20
- package/src/store/commits.ts +5 -4
- package/src/store/composite.ts +7 -2
- package/src/store/consts.local.ts +1 -0
- package/src/store/fold/types.ts +44 -0
- package/src/store/fold.ts +214 -212
- package/src/store/index.ts +1 -0
- package/src/store/memory.ts +0 -0
- package/src/store/types.local.ts +3 -0
- package/src/store/types.ts +16 -1
- package/src/types.local.ts +7 -0
- package/src/types.ts +139 -6
- package/src/utils/access/types.ts +30 -0
- package/src/utils/access.ts +33 -0
- package/src/utils/guard/types.ts +21 -0
- package/src/utils/guard.ts +44 -31
- package/src/utils/handler/types.ts +28 -0
- package/src/utils/handler.ts +35 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/request/types.ts +23 -0
- package/src/utils/request.ts +40 -0
- package/src/utils/scope.ts +39 -42
- package/src/utils/types.ts +8 -0
- package/tests/access.spec.ts +160 -0
- package/tests/app.spec.ts +112 -0
- package/tests/conformance.spec.ts +47 -0
- package/tests/create-example.spec.ts +202 -0
- package/tests/creator.spec.ts +257 -0
- package/tests/entrypoints.spec.ts +5 -0
- package/tests/memory-store.spec.ts +52 -0
- package/tests/narrowing.spec.ts +94 -0
- package/tests/plugins.spec.ts +4 -3
- package/tests/writes.spec.ts +140 -0
- package/build/actions/cards.d.ts +0 -16
- package/build/actions/cards.d.ts.map +0 -1
- package/build/actions/cards.js +0 -32
- package/build/actions/cards.js.map +0 -1
- package/build/actions/schemas.d.ts +0 -6
- package/build/actions/schemas.d.ts.map +0 -1
- package/build/actions/schemas.js +0 -9
- package/build/actions/schemas.js.map +0 -1
- package/build/actions/specs.d.ts +0 -10
- package/build/actions/specs.d.ts.map +0 -1
- package/build/actions/specs.js +0 -14
- package/build/actions/specs.js.map +0 -1
- package/src/actions/cards.ts +0 -52
- package/src/actions/schemas.ts +0 -15
- package/src/actions/specs.ts +0 -23
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The server half of `@owlmeans/planning`: the planning service and its plugin registry, the
|
|
4
4
|
transition executor every write goes through, the in-memory reference store, the commit hub and the
|
|
5
|
-
protocol handlers. A backend appends the service once, binds the handlers to the shared protocol
|
|
5
|
+
protocol handlers, versioned auxiliary resource commands and schema-governed hierarchy. A backend appends the service once, binds the handlers to the shared protocol
|
|
6
6
|
tree, and reads and writes through a scoped facade. A browser or Node client uses
|
|
7
7
|
`@owlmeans/client-planning` against the same tree; a durable store implements the ports and reuses
|
|
8
8
|
the `@owlmeans/server-planning/store` subpath.
|
|
@@ -10,13 +10,16 @@ the `@owlmeans/server-planning/store` subpath.
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
|
-
bun add @owlmeans/server-planning@^0.1.18
|
|
13
|
+
bun add @owlmeans/server-planning@^0.1.18 @owlmeans/planning@^0.1.18 ajv
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Concepts
|
|
17
17
|
|
|
18
|
-
- **Facade** — `
|
|
19
|
-
with no scope argument on any method.
|
|
18
|
+
- **Facade** — `ctx.service<PlanningHostService>(PLANNING_SERVICE).for(scope)`: every read and
|
|
19
|
+
write of one organization entity, with no scope argument on any method. `context.planning()` is
|
|
20
|
+
the same service, but only on a context that went through `appendPlanningService`; a service
|
|
21
|
+
registered as a plain one (`makePlanningService()`, a generated target's `services/planning.ts`)
|
|
22
|
+
has no `context.planning`.
|
|
20
23
|
- **Executor** — the only write path: normalize → idempotency → resolve → plugins' `before` →
|
|
21
24
|
validate → id → code → changes → seq CAS → append → project → receipt. Nothing is appended by a
|
|
22
25
|
refusal.
|
|
@@ -26,6 +29,8 @@ bun add @owlmeans/server-planning@^0.1.18-rc.9 @owlmeans/planning@^0.1.18-rc.8 a
|
|
|
26
29
|
`after` chain exactly once and publishes a commit event.
|
|
27
30
|
- **Store** — `makeMemoryPlanningStore()` for tests and single-process tools; a durable store in
|
|
28
31
|
anything with more than one process.
|
|
32
|
+
- **Resource facade** — `assignees`, `teams`, `comments` and `mentions` use native versioned ports.
|
|
33
|
+
Organization units serialize dependent writes, schema changes and derived relationships.
|
|
29
34
|
|
|
30
35
|
## Usage
|
|
31
36
|
|
|
@@ -37,6 +42,8 @@ import { makePlanningProtocols } from '@owlmeans/planning'
|
|
|
37
42
|
export const planningProtocols = makePlanningProtocols({
|
|
38
43
|
base: { alias: 'app:planning', path: '/planning' },
|
|
39
44
|
guards: DEFAULT_GUARD,
|
|
45
|
+
resources: true,
|
|
46
|
+
definitions: true,
|
|
40
47
|
})
|
|
41
48
|
```
|
|
42
49
|
|
|
@@ -90,47 +97,113 @@ ensurePlanningService(context).use({
|
|
|
90
97
|
Hand-written handler and the memory store in a test:
|
|
91
98
|
|
|
92
99
|
```ts
|
|
93
|
-
import { appendPlanningService,
|
|
100
|
+
import { appendPlanningService, planningHandlerOf } from '@owlmeans/server-planning'
|
|
94
101
|
import { makeMemoryPlanningStore } from '@owlmeans/server-planning/store'
|
|
95
102
|
|
|
96
103
|
appendPlanningService(testContext, { store: makeMemoryPlanningStore({ sync: false }), plugins: [fixtures] })
|
|
97
|
-
const cards = await planningFor(
|
|
104
|
+
const cards = await planningHandlerOf(ctx).planningFor(req, { channel: 'web' }).cards.list({ parent: projectId })
|
|
98
105
|
```
|
|
99
106
|
|
|
107
|
+
## Native planning resources
|
|
108
|
+
|
|
109
|
+
Register assignee type schemas through `schemas.assigneeTypes` or the facade's definitions.
|
|
110
|
+
The scoped facade exposes versioned `assignees.create/update/retire`, `teams.create/update/remove`,
|
|
111
|
+
`comments.create/update/remove` and derived `mentions` reads/rebuilds. Resource updates and
|
|
112
|
+
removals require `{ version: record.version }`; they do not allocate card transitions.
|
|
113
|
+
|
|
114
|
+
Supply a verified `assigneeId` in the trusted scope for comment authors. Hosts may also supply
|
|
115
|
+
`defaultAssigneeId`; `@owlmeans/planning-auth` resolves verified authentication subjects and groups
|
|
116
|
+
to planning ids and can attribute missing card reporters. Never accept author or organization
|
|
117
|
+
claims from an untrusted resource request. Human authentication requirements come from assignee
|
|
118
|
+
type schemas, while nicknames identify participants only within an organization entity.
|
|
119
|
+
|
|
120
|
+
Use `teams.addMember/removeMember`, `teams.attach/detach`, `teams.members`, `teams.projects` and
|
|
121
|
+
`teams.assignees`. They reuse the relationship store and preserve reusable organization teams.
|
|
122
|
+
Schema reference fields maintain their canonical edges and named inverse views in the same
|
|
123
|
+
organization unit. Explicit relationship writes cannot contradict a field-authoritative edge.
|
|
124
|
+
|
|
125
|
+
`parent`, `parents` and derived `parentType` are the hierarchy. Schema parent/child rules validate
|
|
126
|
+
creates and reparenting; recursive project scope is checked before paging, including comments,
|
|
127
|
+
history, specifications and relationships. Project `mode` is access-policy metadata, separate
|
|
128
|
+
from status. Deleting a card removes its comments and mention cache. Project purge follows
|
|
129
|
+
current ancestry and retains unrelated organization assignees, reusable teams and reparented
|
|
130
|
+
survivors, even when an older log or edge caches the deleted project id.
|
|
131
|
+
|
|
132
|
+
Mount `resources: true` and `definitions: true` in the shared protocol tree, then bind stock
|
|
133
|
+
`servePlanningEntrypoints`. Its access resolver authorizes resource commands and conceals records
|
|
134
|
+
outside scope. Stock replies remove native `entityId` metadata from known record/envelope
|
|
135
|
+
positions, while application fields, schema definitions and transition changes remain opaque.
|
|
136
|
+
|
|
100
137
|
## API
|
|
101
138
|
|
|
102
139
|
- Service: `appendPlanningService`, `ensurePlanningService`, `makePlanningService`,
|
|
103
140
|
`planningServiceApi`, `makePluginRegistry`, `makeStoreFacade`, `executeTransition`,
|
|
104
|
-
`
|
|
105
|
-
|
|
141
|
+
`creatorHelper` (`creatorOf`, `withCreator`, `assertCreatorFixed`), `PlanningServiceOptions`, `PlanningHostService`,
|
|
142
|
+
`PlanningRuntime`, `PluginRegistry`
|
|
143
|
+
- Handlers (one per file under `actions/`): `servePlanningEntrypoints`, `listSchemas`, `listCards`,
|
|
106
144
|
`summarizeCards`, `getCard`, `listCardTransitions`, `listCardSpecifications`, `getSpecification`,
|
|
107
|
-
`listSpecificationRevisions`, `listLinks`, `getTransition`, `executePlanning`, `
|
|
108
|
-
`
|
|
109
|
-
|
|
110
|
-
`
|
|
111
|
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
`
|
|
115
|
-
`
|
|
116
|
-
|
|
145
|
+
`listSpecificationRevisions`, `listLinks`, `getTransition`, `executePlanning`, `getCommit`,
|
|
146
|
+
`watchCommits`, `defineSchemas`, `applySchemaRequest`; `executionHelper` (`wireExecution`,
|
|
147
|
+
`executeOptionsOf`, `assertExecutionWrites`, `assertExecutionGranted`); `PlanningHandlerOptions`,
|
|
148
|
+
`PlanningScopeExtractor`, `PlanningAccessResolver`, `PlanningAccess`, `PlanningAccessGrants`,
|
|
149
|
+
`PlanningGrant`
|
|
150
|
+
- Scope: `planningHandlerOf(ctx)` (`planningServiceOf`, `handlerScopeOf`, `handlerFacade`,
|
|
151
|
+
`planningFor`), `makeRequestScope(req)` (`scopeOf`, `accessScopeOf`, `actorOf`),
|
|
152
|
+
`makePlanningAccessModel(access)` (`assertGranted`, `writableIn`, `assertWrites`), `guardHelper`
|
|
153
|
+
(`concealed`, `assertScope`, `notFoundOf`, `clampSeconds`), `projectCriteriaOf`
|
|
154
|
+
- Data-defined schemas: `makeDefinitions`, `makeSchemaViews`, `SchemaViews`, `SchemaViewsOptions`
|
|
155
|
+
- Projection: `makeProjectionProcessor`, `makePlanningQueueHooks`, `ProjectionOptions`
|
|
156
|
+
- Constants: `DEFAULT_ALIAS`, `MEMORY_STORE_ALIAS`, `DEFAULT_COMMIT_MEMORY`, `COMMIT_POLL_LADDER`,
|
|
157
|
+
`DEFAULT_SCHEMA_VIEWS`
|
|
158
|
+
- `@owlmeans/server-planning/store`: `makeMemoryPlanningStore`, `foldHelper` (`foldPending`,
|
|
159
|
+
`failPending`, `revisionsFromLog`, `commitEventOf`), `makeCommitHub`, `makeCompositeStore`,
|
|
160
|
+
`wantsSpecifications`,
|
|
161
|
+
and the types `BindablePlanningStore`, `CommitListener`, `CommitHub`, `CommitHubOptions`,
|
|
162
|
+
`FoldOptions`, `FoldResult`, `MemoryPlanningStore`, `MemoryPlanningStoreOptions`, `StoreRoute`
|
|
163
|
+
- `@owlmeans/server-planning/conformance`: `planningConformance`, `conformanceCasesFor`,
|
|
164
|
+
`planningConformancePlugin`, `conformanceClock`, `ConformanceFailure`, `assertHelper` (`check`,
|
|
165
|
+
`same`, `sameSet`, `rejects`), the library fixtures (`LIBRARY`, `conformanceFixturesOf(facade)` —
|
|
166
|
+
`createBranch`, `createBook`, `transit`)
|
|
167
|
+
and the types `ConformanceCase`, `ConformanceSubject`, `ConformanceCapability`
|
|
168
|
+
- The former plain functions (`planningFor`, `foldPending`, `creatorOf`, `scopeOf`, `concealed`,
|
|
169
|
+
`planningQueueHooks`, `check`, `createBranch`, …) remain as deprecated delegates.
|
|
117
170
|
|
|
118
171
|
## Common pitfalls
|
|
119
172
|
|
|
173
|
+
- A create's draft goes under `card` — `draft` is refused as `planning:malformed:create-without-draft`.
|
|
174
|
+
- `{ wait: true }` is the SECOND argument of `execute`; inside the execution it is ignored and the
|
|
175
|
+
receipt returns before the commit.
|
|
176
|
+
- A type or flow declaration has no `name` or `key`: the display name is `label`, the key is `type`
|
|
177
|
+
(a type) or `id` (a flow). `definitions.define` refuses anything else with `SchemaInvalid`; a code
|
|
178
|
+
plugin's registry does not check, so keep the literals typed.
|
|
179
|
+
|
|
120
180
|
- `after` hooks run where the transition is FOLDED, once per commit — register the same plugins in
|
|
121
181
|
every folding process, and never run hooks from a commit-bus subscription.
|
|
182
|
+
- Organization locks are released before `after` hooks, so a hook may write into the same scope.
|
|
122
183
|
- The memory store is per-process heap: wrong for multiple processes or restarts.
|
|
123
184
|
- `actor` comes from the scope; a wire `actor` or `createdBy` is ignored.
|
|
185
|
+
- A create's `createdBy` defaults to the scope's subject (`profileId`, else `userId`, else the
|
|
186
|
+
scope actor's) on every path — in-process facade and HTTP alike; an in-process draft that names
|
|
187
|
+
one keeps it, a scope naming nobody leaves it unset.
|
|
188
|
+
- After the create `createdBy` never moves: an execution naming it in `changes` or `unset` is
|
|
189
|
+
refused with `planning:immutable:createdBy` — over HTTP, in process and from a `before` plugin.
|
|
124
190
|
- Another entity's record is `WorkcardNotFound`, never a permission error.
|
|
125
191
|
- Give retried writes a `key` — it is checked before validation, so a retry answers the first receipt.
|
|
126
192
|
- `expectSeq` compares against `head`; a stale one is `WorkcardConflict`, re-read and retry.
|
|
127
193
|
- Call `appendPlanningService` before any `ensurePlanningService`, or the appended host replaces
|
|
128
194
|
what was registered on the default one.
|
|
195
|
+
- `opts.access` makes the resolver the only source of the organization; a `grants` object refuses
|
|
196
|
+
every flag it leaves out.
|
|
197
|
+
- A durable store is not done until it passes `@owlmeans/server-planning/conformance`.
|
|
198
|
+
- Resource writes require their current `version`; assignee/team `fields` replace the previous
|
|
199
|
+
object, while card `changes.fields` merge. A field-backed relationship is changed through its
|
|
200
|
+
field, not by writing an independent link.
|
|
129
201
|
|
|
130
202
|
## Related packages
|
|
131
203
|
|
|
132
204
|
- `@owlmeans/planning` — records, flows, fold, query language, protocol tree, models
|
|
133
205
|
- `@owlmeans/client-planning` — the remote facade and state mirror
|
|
206
|
+
- `@owlmeans/planning-postgres` — the durable Postgres store
|
|
134
207
|
- `@owlmeans/server-job`, `@owlmeans/queue` — job feeds and the projection queue
|
|
135
208
|
|
|
136
209
|
<!-- owlmeans:agent-guidance:start -->
|
|
@@ -141,7 +214,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
141
214
|
your project's skill store (`.agents/skills/`):
|
|
142
215
|
|
|
143
216
|
```sh
|
|
144
|
-
npx @owlmeans/agent-skills@^0.1.18
|
|
217
|
+
npx @owlmeans/agent-skills@^0.1.18
|
|
145
218
|
```
|
|
146
219
|
|
|
147
220
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion":
|
|
2
|
+
"schemaVersion": 3,
|
|
3
3
|
"package": "@owlmeans/server-planning",
|
|
4
|
-
"version": "0.1.18
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18",
|
|
5
|
+
"generatedAt": "2026-10-28T00:31:34.706Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -10,7 +10,108 @@
|
|
|
10
10
|
"name": "server-planning",
|
|
11
11
|
"category": "package-specific",
|
|
12
12
|
"file": "skills/server-planning/SKILL.md",
|
|
13
|
-
"canonicalPath": ".agents/skills/server-planning/SKILL.md"
|
|
13
|
+
"canonicalPath": ".agents/skills/server-planning/SKILL.md",
|
|
14
|
+
"parts": [
|
|
15
|
+
{
|
|
16
|
+
"name": "auxiliary",
|
|
17
|
+
"file": "skills/server-planning/parts/auxiliary.md",
|
|
18
|
+
"description": "Assignees, teams, comments and mentions on native store ports, the reply projection, hierarchy admission and project purge.",
|
|
19
|
+
"purposes": [
|
|
20
|
+
"planning",
|
|
21
|
+
"data"
|
|
22
|
+
],
|
|
23
|
+
"mentions": [
|
|
24
|
+
"facade.assignees",
|
|
25
|
+
"facade.teams",
|
|
26
|
+
"facade.comments",
|
|
27
|
+
"facade.mentions",
|
|
28
|
+
"manageAssignees",
|
|
29
|
+
"manageTeams",
|
|
30
|
+
"defaultAssigneeId",
|
|
31
|
+
"makePlanningAuth",
|
|
32
|
+
"guardHelper.reply"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "executor",
|
|
37
|
+
"file": "skills/server-planning/parts/executor.md",
|
|
38
|
+
"description": "The transition executor step by step (refusal order, actor and createdBy stamping) and the plugin seam (registration, order, store routing, before/after hooks).",
|
|
39
|
+
"purposes": [
|
|
40
|
+
"planning",
|
|
41
|
+
"server-api"
|
|
42
|
+
],
|
|
43
|
+
"mentions": [
|
|
44
|
+
"executeTransition",
|
|
45
|
+
"creatorHelper",
|
|
46
|
+
"makePluginRegistry",
|
|
47
|
+
"ensurePlanningService",
|
|
48
|
+
"PlanningRefused",
|
|
49
|
+
"mintCode",
|
|
50
|
+
"WorkcardConflict"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "schemas",
|
|
55
|
+
"file": "skills/server-planning/parts/schemas.md",
|
|
56
|
+
"description": "Data-defined card types and flows — the resolved layer per organization and project, the facade's definitions, retiring a flow.",
|
|
57
|
+
"purposes": [
|
|
58
|
+
"data",
|
|
59
|
+
"planning",
|
|
60
|
+
"config"
|
|
61
|
+
],
|
|
62
|
+
"mentions": [
|
|
63
|
+
"makeDefinitions",
|
|
64
|
+
"makeSchemaViews",
|
|
65
|
+
"schemasFor",
|
|
66
|
+
"scopedSchemaHelper",
|
|
67
|
+
"definitions",
|
|
68
|
+
"SchemaInUse"
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "serving",
|
|
73
|
+
"file": "skills/server-planning/parts/serving.md",
|
|
74
|
+
"description": "The commit feed, request scope and security, the access resolver (projects, writes, grants), and mounting the handlers in a target.",
|
|
75
|
+
"purposes": [
|
|
76
|
+
"server-api",
|
|
77
|
+
"auth",
|
|
78
|
+
"planning"
|
|
79
|
+
],
|
|
80
|
+
"mentions": [
|
|
81
|
+
"servePlanningEntrypoints",
|
|
82
|
+
"planningHandlerOf",
|
|
83
|
+
"PlanningAccessResolver",
|
|
84
|
+
"makeRequestScope",
|
|
85
|
+
"makePlanningAccessModel",
|
|
86
|
+
"guardHelper",
|
|
87
|
+
"executionHelper",
|
|
88
|
+
"PlanningForbidden"
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "stores",
|
|
93
|
+
"file": "skills/server-planning/parts/stores.md",
|
|
94
|
+
"description": "The in-memory store, writing a durable or foreign store (ports, foldHelper.foldPending, the commit hub, the conformance suite), and testing against the memory store.",
|
|
95
|
+
"purposes": [
|
|
96
|
+
"data",
|
|
97
|
+
"planning",
|
|
98
|
+
"test"
|
|
99
|
+
],
|
|
100
|
+
"mentions": [
|
|
101
|
+
"makeMemoryPlanningStore",
|
|
102
|
+
"PlanningStore",
|
|
103
|
+
"foldHelper",
|
|
104
|
+
"makeCommitHub",
|
|
105
|
+
"makeCompositeStore",
|
|
106
|
+
"planningConformance",
|
|
107
|
+
"conformanceCasesFor",
|
|
108
|
+
"planningConformancePlugin",
|
|
109
|
+
"makeProjectionProcessor",
|
|
110
|
+
"@owlmeans/server-planning/store",
|
|
111
|
+
"@owlmeans/server-planning/conformance"
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
]
|
|
14
115
|
}
|
|
15
116
|
]
|
|
16
117
|
}
|
|
@@ -8,12 +8,12 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/server-planning
|
|
9
9
|
|
|
10
10
|
**Layer:** Server
|
|
11
|
-
**Install:** `"@owlmeans/server-planning": "^0.1.18
|
|
11
|
+
**Install:** `"@owlmeans/server-planning": "^0.1.18"` in `dependencies` (`ajv` is a peer)
|
|
12
12
|
|
|
13
13
|
The general implementation of `@owlmeans/planning`: the planning service (a plugin host), the
|
|
14
14
|
scoped facade, the executor every write goes through, the in-memory reference store, the commit
|
|
15
15
|
hub and the protocol handlers. It holds no database code — a durable store implements the ports and
|
|
16
|
-
reuses the `./store` subpath (`foldPending`, `makeCommitHub`) without pulling fastify in.
|
|
16
|
+
reuses the `./store` subpath (`foldHelper.foldPending`, `makeCommitHub`) without pulling fastify in.
|
|
17
17
|
|
|
18
18
|
## Key exports
|
|
19
19
|
|
|
@@ -22,15 +22,20 @@ reuses the `./store` subpath (`foldPending`, `makeCommitHub`) without pulling fa
|
|
|
22
22
|
| `appendPlanningService(ctx, opts?, alias?)` | Registers the lazy host service and `ctx.planning()` |
|
|
23
23
|
| `ensurePlanningService(ctx, alias?)` | Idempotent — what a plugin package calls before `use` |
|
|
24
24
|
| `makePlanningService(opts?, alias?)`, `planningServiceApi(opts, self)` | The service, and its body for a specialised service |
|
|
25
|
-
| `PlanningServiceOptions` | `{ store?, plugins?, schemas?, hooks?, ids?, now? }` |
|
|
25
|
+
| `PlanningServiceOptions` | `{ store?, plugins?, schemas?, hooks?, ids?, now? }` — `schemas.app` is the first source the plugins' `schemas.app` merge over |
|
|
26
26
|
| `makePluginRegistry`, `makeStoreFacade`, `executeTransition` | The pieces the service is made of |
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
|
|
|
27
|
+
| `creatorHelper` — `.creatorOf(scope)`, `.withCreator(exec, scope)`, `.assertCreatorFixed(exec)` | The subject a create is stamped with as `createdBy`, the defaulting the executor applies, and its refusal of any later move |
|
|
28
|
+
| `servePlanningEntrypoints(protocols, opts?)` | One binding per protocol of a `makePlanningProtocols` tree (`schema.define` too when declared) |
|
|
29
|
+
| `planningHandlerOf(ctx).planningFor(req, extra?)` | The request-scoped facade for a hand-written handler |
|
|
30
|
+
| `PlanningHandlerOptions` | `{ service?, event?, maxPoll?, scope?(req, ctx), access?(req, ctx) }` |
|
|
31
|
+
| `PlanningAccessResolver`, `PlanningAccess`, `PlanningAccessGrants`, `PlanningGrant` | The optional access decision a hosting app supplies |
|
|
32
|
+
| `listCards` … `executePlanning`, `getCommit`, `watchCommits`, `listSchemas`, `defineSchemas`, `applySchemaRequest` | The handlers, to bind one by hand |
|
|
33
|
+
| `makeRequestScope(req)` — `.scopeOf(extra?)`, `.accessScopeOf(access, extra?)`, `.actorOf()`; `planningHandlerOf(ctx).handlerScopeOf`; `makePlanningAccessModel(access)` — `.assertGranted`, `.assertWrites`, `.writableIn`; `executionHelper` — `.assertExecutionGranted`, `.assertExecutionWrites`; `guardHelper` — `.concealed(run)`, `.clampSeconds` | Scope and security helpers |
|
|
34
|
+
| `makeDefinitions(runtime, facade)`, `makeSchemaViews(opts)`, `projectCriteriaOf(projects, through?)` | Data-defined schemas and the project narrowing |
|
|
35
|
+
| `makeProjectionProcessor(ctx, opts?)`, `makePlanningQueueHooks(ctx, opts?)` | A generic projection job body and its `onJobDead` |
|
|
36
|
+
| `./queue`: `declarePlanningQueue(cfg, opts?)`, `PLANNING_PROJECTION_QUEUE` (`planning-projection`), `PLANNING_PROJECT_JOB` (`planning:project`), `ProjectionRequest` | The projection queue's shared address; each process chooses separately whether to listen |
|
|
37
|
+
| `./store`: `makeMemoryPlanningStore`, `foldHelper` (`.foldPending`, `.failPending`, `.revisionsFromLog`, `.commitEventOf`), `makeCommitHub`, `makeCompositeStore`, `wantsSpecifications` | What stores are built from |
|
|
38
|
+
| `./conformance`: `planningConformance`, `conformanceCasesFor`, `planningConformancePlugin`, `conformanceClock`, `ConformanceFailure` | The store conformance suite, runner-agnostic |
|
|
34
39
|
|
|
35
40
|
## Wiring
|
|
36
41
|
|
|
@@ -58,39 +63,69 @@ context.registerEntrypoints(servePlanningEntrypoints(planningProtocols, {
|
|
|
58
63
|
`ensurePlanningService(ctx).use(plugin)` works from `makeContext`, before `init()`.
|
|
59
64
|
- Call `appendPlanningService` BEFORE any `ensurePlanningService`: `ensure` registers a default
|
|
60
65
|
host when none exists, and a later `append` replaces it together with whatever was `use`d on it.
|
|
61
|
-
-
|
|
62
|
-
with no scope argument on any method.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
66
|
+
- **The facade:** `ctx.service<PlanningHostService>(PLANNING_SERVICE).for({ entityId, profileId?,
|
|
67
|
+
channel?, actor? })` → a `PlanningFacade` with no scope argument on any method. That form works
|
|
68
|
+
however the service was registered. `ctx.planning()` is only a shortcut that `appendPlanningService`
|
|
69
|
+
/ `appendPostgresPlanning` install (typed `WithPlanningService`); a context that registered
|
|
70
|
+
`makePlanningService()` / `makePostgresPlanningService()` as a plain service — a generated target's
|
|
71
|
+
`services/planning.ts` — has no `ctx.planning` at all. In a request handler use
|
|
72
|
+
`planningHandlerOf(ctx).planningFor(req)`: the entity comes from `makeEntityScope(req).requireEntityKey()`, never the token.
|
|
73
|
+
|
|
74
|
+
## Creating and reading a card
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { IntrinsicStatus, PLANNING_SERVICE, TransitionAction, WorkcardKind } from '@owlmeans/planning'
|
|
78
|
+
import type { PlanningHostService } from '@owlmeans/server-planning'
|
|
79
|
+
|
|
80
|
+
const planning = ctx.service<PlanningHostService>(PLANNING_SERVICE).for({ entityId, profileId })
|
|
81
|
+
|
|
82
|
+
const shed = await planning.execute({
|
|
83
|
+
action: TransitionAction.Create,
|
|
84
|
+
card: { kind: WorkcardKind.Project, type: 'shed:shed', title: 'Maple Street shed' },
|
|
85
|
+
}, { wait: true })
|
|
86
|
+
const drill = await planning.execute({
|
|
87
|
+
action: TransitionAction.Create,
|
|
88
|
+
card: { kind: WorkcardKind.Card, type: 'shed:tool', parent: shed.card!.id!, title: 'Cordless drill',
|
|
89
|
+
fields: { brand: 'Acme' } },
|
|
90
|
+
}, { wait: true }) // drill.card — the committed record, status 'available'
|
|
91
|
+
|
|
92
|
+
await planning.cards.get(drill.card!.id!)
|
|
93
|
+
await planning.cards.list({ parent: shed.card!.id!, type: 'shed:tool' })
|
|
94
|
+
await planning.execute({ action: TransitionAction.Transit, card: drill.card!.id!, transition: 'lend' }, { wait: true })
|
|
95
|
+
|
|
96
|
+
// A card type and a flow as DATA, scoped to this shed — `definitions` exists only where the store
|
|
97
|
+
// keeps schemas (planning-postgres; the memory store with `schemas: true`), and the shed's cards may
|
|
98
|
+
// use them when its project type says `scopedCardTypes: true` (or lists the type in `cardTypes`).
|
|
99
|
+
await planning.definitions!.define({
|
|
100
|
+
flows: [{
|
|
101
|
+
id: 'shed:repair', version: 1, label: 'Repair',
|
|
102
|
+
statuses: [
|
|
103
|
+
{ key: 'reported', intrinsic: IntrinsicStatus.Planned, initial: true, label: 'Reported' },
|
|
104
|
+
{ key: 'fixed', intrinsic: IntrinsicStatus.Closed, terminal: true, label: 'Fixed' },
|
|
105
|
+
],
|
|
106
|
+
transitions: [{ name: 'fix', from: ['reported'], to: 'fixed', label: 'Mark fixed', explicit: true }],
|
|
107
|
+
}],
|
|
108
|
+
types: [{
|
|
109
|
+
type: 'shed:repair', kind: WorkcardKind.Card, version: 1, label: 'Repair ticket',
|
|
110
|
+
fields: { type: 'object', properties: { tool: { type: 'string' } }, additionalProperties: false },
|
|
111
|
+
flows: ['shed:repair'], specifications: [],
|
|
112
|
+
}],
|
|
113
|
+
}, { project: shed.card!.id! })
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
| Wrong | What happens | Right |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `execute({ action, draft: { … } })` | refused: `planning:malformed:create-without-draft` | the draft goes under `card` |
|
|
119
|
+
| `execute({ …, wait: true })` | in process the key is ignored: the receipt returns before the commit, with no `card` — hidden while an inline-folding store (`planning-postgres`) lands at once, exposed the first time another process holds the card's fold | `execute({ … }, { wait: true })` — only the wire body (`ExecuteRequest`) carries `wait` inside |
|
|
120
|
+
| `ctx.planning().for(…)` in a target | `ctx.planning is not a function` | `ctx.service<PlanningHostService>(PLANNING_SERVICE).for(…)` |
|
|
121
|
+
| `{ id, name: 'Repair', … }` on a flow, `{ key, name, … }` on a type | `define` refuses with `SchemaInvalid` (closed schemas); a code plugin's registry does not validate, so the key is silently dropped | a flow's key is `id`, a type's is `type`, the display name is `label` on both |
|
|
122
|
+
| `{ …, description: '…' }` on a type or a flow | `SchemaInvalid` (closed schemas) | `app: { description }`, with `schemas.app.type` / `.flow` declaring `description` (`planning` → types part) |
|
|
123
|
+
| `action: 'create'` | a type error — `TransitionAction` is an enum | `action: TransitionAction.Create` |
|
|
124
|
+
|
|
125
|
+
Every wrong form is a compile error against the typed facade (`PlanningFacade`,
|
|
126
|
+
`TransitionExecution`, `WorkcardTypeSchema`); it reaches run time only through a helper or a context
|
|
127
|
+
typed `any`, so never type one that way. `tests/create-example.spec.ts` runs this example and each
|
|
128
|
+
wrong form as written.
|
|
94
129
|
|
|
95
130
|
## Idempotency and `expectSeq`
|
|
96
131
|
|
|
@@ -100,104 +135,20 @@ in-process caller may add `agent`/`runId` on the execution. A handler never pass
|
|
|
100
135
|
see the same `seq` but only one wins the head. `null` or omitted opts out; a model fills it from
|
|
101
136
|
its record.
|
|
102
137
|
|
|
103
|
-
## The plugin seam
|
|
104
|
-
|
|
105
|
-
| Concern | Rule |
|
|
106
|
-
|---|---|
|
|
107
|
-
| Registration | `use(plugin)` replaces by `name`; schemas are contributed at `use` time (later type/flow id wins) |
|
|
108
|
-
| Order | `order ?? 50` ascending, registration order among equals |
|
|
109
|
-
| Store routing | `store(type)` = the first plugin whose `owns(type)` is true AND that supplies a `store`; else the default store. A store factory is resolved once |
|
|
110
|
-
| Reads | By id: the default store, then each owned store. A list naming one owned type (or several owned by one store) goes there; any other list — a cross-type query — is the default store's alone |
|
|
111
|
-
| `mintCode` | First non-`undefined` answer wins |
|
|
112
|
-
| `before` | Every plugin in order; may return a replaced execution; a `ResilientError` passes through, anything else becomes `PlanningRefused('<plugin>:<message>')` |
|
|
113
|
-
| `after` | Every plugin in order, once per COMMITTED transition; errors logged, never rethrown |
|
|
114
|
-
|
|
115
|
-
**Where `after` runs.** The service's `committed(event)` runs the chain, and it is called by the
|
|
116
|
-
process that FOLDS the transition — the memory store inside `project()`, a queued store's projection
|
|
117
|
-
job after marking the commit. Never subscribe a process to the commit bus to run hooks: that fires
|
|
118
|
-
once per subscribed process. Every process that folds registers the same plugins; each commit is
|
|
119
|
-
folded by one of them, so each hook fires once. `hooks: false` makes a process fold without running
|
|
120
|
-
them. A hook sees `ctx.transition` (read back, so `actor.channel` is available) and a facade scoped
|
|
121
|
-
to the event's entity acting as this process's `cfg.service`.
|
|
122
|
-
|
|
123
|
-
The service hands `committed` to a store through `store.bind?.(listener)` the first time it resolves
|
|
124
|
-
the store; `bind` replaces the listener, so a store has exactly one.
|
|
125
|
-
|
|
126
|
-
## The memory store
|
|
127
|
-
|
|
128
|
-
`makeMemoryPlanningStore({ sync?, ids?, now?, seed?, onCommitted?, alias? })`
|
|
129
|
-
|
|
130
|
-
- Cards and projects in one map, specifications in their own, relationships in a third; every read
|
|
131
|
-
goes through the `@owlmeans/resource` query engine. A card list includes specifications only when
|
|
132
|
-
the criteria asks for `kind: specification` or a `category`.
|
|
133
|
-
- `sync` (default) folds, publishes and runs `committed` inside `project()`, serialized per card.
|
|
134
|
-
`sync: false` folds nothing until `flush(card?)` — a queued store's shape, for tests that need a
|
|
135
|
-
commit to stay pending.
|
|
136
|
-
- A project `delete` purges the project, everything whose `parents` reach it, their specifications,
|
|
137
|
-
links and log; the commit hub remembers the settled event so the waiter still gets its answer.
|
|
138
|
-
- **It is wrong for more than one process or a restart** — the log, the cards and the commit feed
|
|
139
|
-
are this process's heap. Use it for tests, tools and single-process demos.
|
|
140
|
-
|
|
141
|
-
## Writing a durable store
|
|
142
|
-
|
|
143
|
-
- Implement `PlanningStore` (`transitions`, `cards`, `specs`, `links`, `commits`, `newId`). `newId`
|
|
144
|
-
answers the id format the host's references need (ObjectId hex for Mongo).
|
|
145
|
-
- `transitions.list` treats `sinceSeq` as `seq > sinceSeq`; `nextSeq` is the CAS against `head` and
|
|
146
|
-
answers 1 for a card that has no log.
|
|
147
|
-
- The projection body is `foldPending(store, cardId, entityId, { onCommitted, publish, touch, limit })`:
|
|
148
|
-
it applies pending transitions with `applyTransition` only, marks each committed or failed,
|
|
149
|
-
publishes, then runs `onCommitted`. The caller owns single flight per card. Strip `record` from
|
|
150
|
-
the event before a cross-process bus.
|
|
151
|
-
- **A failed transition never blocks the card.** The fold marks it `failed`, publishes a failed
|
|
152
|
-
event, and goes PAST it: the card's `seq` advances to that transition with every other value as
|
|
153
|
-
it was, and the next transition folds normally. Without that, every later transition fails as
|
|
154
|
-
out of order and the card is stuck for good. Only two cases stop short: a failed `create` leaves
|
|
155
|
-
no card to advance, and a store that refuses the advancing write stops the fold with `followUp`,
|
|
156
|
-
leaving the rest pending for a retry. `revisionsFromLog` replays past failed transitions the same
|
|
157
|
-
way.
|
|
158
|
-
- `failPending` is what a dead projection job must do, or waiters hang to their timeout.
|
|
159
|
-
- `makeCommitHub({ status, remember?, ladder? })` is the `CommitSource`: feed `publish` from the bus,
|
|
160
|
-
answer `status` from transition rows. `wait` subscribes first, polls once, then climbs the ladder.
|
|
161
|
-
- `revisionsFromLog(transitions, limit?)` answers `specs.revisions`.
|
|
162
|
-
- `makeProjectionProcessor(ctx)` + `planningQueueHooks(ctx)` are the store-agnostic job body and
|
|
163
|
-
`onJobDead`; a store with an admission CAS wraps its own around them.
|
|
164
|
-
|
|
165
|
-
## A foreign provider
|
|
166
|
-
|
|
167
|
-
Only `cards` is required. A plugin that `owns` its types and supplies a store without
|
|
168
|
-
`transitions` can be read through the facade; writes to it refuse with `PlanningUnsupported` —
|
|
169
|
-
mapping external records (`mappers`) belongs to that plugin's own store adapter.
|
|
170
|
-
|
|
171
|
-
## The commit feed
|
|
172
|
-
|
|
173
|
-
- **Poll** — `commit.get` answers the status; with `?wait=<s>` (clamped to `maxPoll`, default 55)
|
|
174
|
-
it subscribes, re-polls, and holds until the commit settles or the time is up, answering `pending`
|
|
175
|
-
then — never an error.
|
|
176
|
-
- **Notify** — `commit.events` pushes `planning-commit` frames (or `opts.event`) filtered by the
|
|
177
|
-
authenticated entity and optional `project`/`card` query values; a frame without `record` gets the
|
|
178
|
-
card as it reads now. The subscription is released on the socket's `close` frame.
|
|
179
|
-
- `execute` with `wait: true` holds at most `maxPoll` seconds whatever `timeout` the body asked for.
|
|
180
|
-
|
|
181
|
-
## Scope and security
|
|
182
|
-
|
|
183
|
-
- `entityId` is `requireEntityKey(req)`; `opts.scope(req, ctx)` adds a `channel` or `service` and
|
|
184
|
-
can never replace the entity. `actor` and a create's `createdBy` come from the request, never the
|
|
185
|
-
body.
|
|
186
|
-
- Another entity's card, specification or transition answers `WorkcardNotFound` — the same as an
|
|
187
|
-
absent id — on reads and writes alike (`concealed`).
|
|
188
|
-
- Queries arrive in their wire form and are decoded with `decode*Query` before `criteriaOf`, which
|
|
189
|
-
always adds the scope's `entityId`.
|
|
190
|
-
|
|
191
|
-
## Testing
|
|
192
|
-
|
|
193
|
-
Category A. Build one real context with `makeBasicContext` + `appendPlanningService({ store:
|
|
194
|
-
makeMemoryPlanningStore({ now }), plugins: [fixtures] })`, and run a handler through
|
|
195
|
-
`handler.bind({ ref: { ctx } })(req, res)` with a request carrying `auth` and `entity`. A monotonic
|
|
196
|
-
`now` keeps log order deterministic; `sync: false` + `flush()` pins pending and hook-count cases.
|
|
197
|
-
|
|
198
138
|
## Related
|
|
199
139
|
|
|
200
140
|
- `planning` — records, flows, the fold, the query language, the protocol tree, the models
|
|
201
141
|
- `client-planning` — the remote facade and the state mirror that talk to these handlers
|
|
142
|
+
- `planning-postgres` — the durable Postgres store built from these pieces
|
|
202
143
|
- `server-job`, `queue` — the declare/serve/feed pattern and the projection queue
|
|
203
144
|
- `resource` — the criteria engine every memory read goes through
|
|
145
|
+
|
|
146
|
+
## Parts
|
|
147
|
+
|
|
148
|
+
| Part | Load it when |
|
|
149
|
+
|---|---|
|
|
150
|
+
| [executor](parts/executor.md) | writing a planning plugin, or diagnosing why a transition was refused or never committed |
|
|
151
|
+
| [stores](parts/stores.md) | choosing or writing a store, or testing planning code |
|
|
152
|
+
| [serving](parts/serving.md) | binding the planning handlers, writing an access resolver, or narrowing what a person sees or writes |
|
|
153
|
+
| [schemas](parts/schemas.md) | defining or retiring types and flows as data |
|
|
154
|
+
| [auxiliary](parts/auxiliary.md) | working with assignees, teams, comments or mentions, or with reparenting and purge |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: auxiliary
|
|
3
|
+
description: Assignees, teams, comments and mentions on native store ports, the reply projection, hierarchy admission and project purge.
|
|
4
|
+
metadata:
|
|
5
|
+
purposes: planning, data
|
|
6
|
+
mentions: facade.assignees, facade.teams, facade.comments, facade.mentions, manageAssignees, manageTeams, defaultAssigneeId, makePlanningAuth, guardHelper.reply
|
|
7
|
+
---
|
|
8
|
+
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
9
|
+
|
|
10
|
+
# @owlmeans/server-planning — Auxiliary resources and hierarchy admission
|
|
11
|
+
|
|
12
|
+
## Auxiliary resources and hierarchy admission
|
|
13
|
+
|
|
14
|
+
`facade.assignees`, `.teams`, `.comments`, `.mentions` use native versioned ports of
|
|
15
|
+
`PlanningStore`. The stock memory, PostgreSQL and Mongo stores implement them. Mutations run
|
|
16
|
+
inside the store's organization `unit`; CAS, nickname/authentication uniqueness, schema layers,
|
|
17
|
+
canonical links and mentions are validated there. Missing ports raise `PlanningUnsupported`. Assignee type changes recheck incoming typed card
|
|
18
|
+
references against the proposed type; a change cannot invalidate a persisted assignment.
|
|
19
|
+
Do not add a second host-specific planning resource CRUD layer.
|
|
20
|
+
|
|
21
|
+
`assignees.list({ retired: false })` means every identity not explicitly retired, including
|
|
22
|
+
records with an absent or null retirement flag. The facade translates it to `{ retired: { $ne: true } }`
|
|
23
|
+
before the native port reads; `retired: true` selects historical identities and an omitted filter
|
|
24
|
+
retains both. Keep this predicate consistent with client mirrors and run its shared conformance
|
|
25
|
+
case against memory, PostgreSQL and Mongo; a bare equality to false hides newly created identities.
|
|
26
|
+
|
|
27
|
+
The request scope supplies trusted `assigneeId` for comment authors and optional
|
|
28
|
+
`defaultAssigneeId` for newly created cards. HTTP handlers stamp actor/organization context;
|
|
29
|
+
`makePlanningAuth(...).scopeFor` resolves OwlMeans identities when that integration is enabled.
|
|
30
|
+
Access resolvers can grant `manageAssignees` and `manageTeams` separately from card writes.
|
|
31
|
+
Comments and project/team attachment require writable containing cards/projects. Auxiliary
|
|
32
|
+
organization lists require authenticated organization scope; do not infer ACLs from project mode.
|
|
33
|
+
|
|
34
|
+
All stock HTTP replies use `guardHelper.reply`, which conceals scope failures and applies the
|
|
35
|
+
typed planning reply projection. Socket commits use the same projection. Organization `entityId`
|
|
36
|
+
stays in native records and verified scopes, and never leaves through records, schema scopes or
|
|
37
|
+
nested receipts. Keep business fields and schema definitions unchanged when projecting replies.
|
|
38
|
+
|
|
39
|
+
Admission and native projection recheck hierarchy, schema fields and endpoint types. Recursive
|
|
40
|
+
visibility follows every project ancestor and filters before paging. Definition record reads
|
|
41
|
+
use the same recursive visibility as cards. Reparenting validates all
|
|
42
|
+
descendants under the prospective schema layer. Ordinary deletion refuses a card with child cards
|
|
43
|
+
(`hierarchy:parent-in-use`, from `makePlanningHierarchy().validate(card, true)`); the card's own
|
|
44
|
+
documents (`hierarchy.documents`) never refuse it — the delete's fold drops them with it, and the
|
|
45
|
+
incoming field-reference check counts them among what goes. Project purge removes its descendants, comments, mentions, scoped schemas and links,
|
|
46
|
+
while retaining organization assignees and reusable teams. Field-backed edges are reconciled in
|
|
47
|
+
the persistence unit; named inverse edges are derived read views, never duplicate persisted rows.
|
|
48
|
+
|
|
49
|
+
Run shared conformance with `resources` and `schemas` capabilities against each native provider;
|
|
50
|
+
cover version races, stable mentions, reusable teams, field references, hierarchy and narrowed ACLs.
|
|
51
|
+
|
|
52
|
+
Project purge uses current card ancestry as ownership. A surviving reparented card retains its
|
|
53
|
+
complete transition history, documents and edges even when their cached project id names its former
|
|
54
|
+
project. Cached project values may select orphaned records, but must never delete surviving records.
|