@onlineapps/conn-orch-validator 7.0.0 → 8.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.
Files changed (102) hide show
  1. package/CHANGELOG.md +2558 -2
  2. package/README.md +1038 -4
  3. package/docs/DESIGN.md +3 -1
  4. package/manifests/biz-service.manifest.json +658 -0
  5. package/manifests/library.manifest.json +324 -0
  6. package/package.json +12 -6
  7. package/src/CookbookTestRunner.js +408 -101
  8. package/src/CookbookTestUtils.js +7 -8
  9. package/src/ServiceReadinessValidator.js +10 -35
  10. package/src/ValidationOrchestrator.js +219 -71
  11. package/src/cli/biz-ci-gate.js +176 -33
  12. package/src/cli/oa-lint-scripts.js +221 -0
  13. package/src/cli/oa-sync-template.js +1020 -0
  14. package/src/cli/oa-validate.js +474 -0
  15. package/src/helpers/README.md +2 -1
  16. package/src/helpers/createServiceReadinessTests.js +60 -4
  17. package/src/index.js +33 -3
  18. package/src/lint/scripts/lintScripts.js +298 -0
  19. package/src/manifest/checks/composeRunnerBlock.js +222 -0
  20. package/src/manifest/checks/composeShape.js +165 -0
  21. package/src/manifest/checks/contractBridge.js +181 -0
  22. package/src/manifest/checks/discoveryOrphan.js +50 -0
  23. package/src/manifest/checks/docsLintBridge.js +553 -0
  24. package/src/manifest/checks/fileAbsent.js +35 -0
  25. package/src/manifest/checks/gitTracked.js +204 -0
  26. package/src/manifest/checks/index.js +111 -0
  27. package/src/manifest/checks/libraryContext.js +226 -0
  28. package/src/manifest/checks/libraryDocs.js +75 -0
  29. package/src/manifest/checks/libraryPackage.js +272 -0
  30. package/src/manifest/checks/librarySource.js +274 -0
  31. package/src/manifest/checks/libraryTests.js +121 -0
  32. package/src/manifest/checks/libraryWorkspace.js +293 -0
  33. package/src/manifest/checks/readmeRegion.js +135 -0
  34. package/src/manifest/checks/scriptHeaders.js +79 -0
  35. package/src/manifest/checks/serviceConfig.js +390 -0
  36. package/src/manifest/checks/serviceConnectors.js +81 -0
  37. package/src/manifest/checks/serviceDb.js +388 -0
  38. package/src/manifest/checks/serviceFiles.js +754 -0
  39. package/src/manifest/checks/serviceIdentityRows.js +351 -0
  40. package/src/manifest/checks/serviceRuntime.js +295 -0
  41. package/src/manifest/checks/serviceScripts.js +213 -0
  42. package/src/manifest/deployabilitySignal.js +121 -0
  43. package/src/manifest/discovery.js +386 -0
  44. package/src/manifest/loadManifest.js +62 -0
  45. package/src/manifest/manifestShape.js +446 -0
  46. package/src/manifest/report.js +245 -0
  47. package/src/manifest/runManifest.js +449 -0
  48. package/src/manifest/serviceIdentity.js +140 -0
  49. package/src/manifest/walk.js +74 -0
  50. package/src/manifest/workspaceRoot.js +242 -0
  51. package/src/mocks/MockMQClient.js +13 -30
  52. package/src/mocks/MockRegistry.js +4 -2
  53. package/src/mocks/MockStorage.js +4 -2
  54. package/src/sync/docsRegion.js +463 -0
  55. package/src/sync/generatedRegion.js +228 -0
  56. package/src/sync/readmeLocation.js +182 -0
  57. package/src/sync/readmePointer.js +477 -0
  58. package/src/sync/serviceTemplate.js +583 -0
  59. package/src/sync/sharedEnv.js +162 -0
  60. package/src/sync/uniformFiles.js +474 -0
  61. package/src/utils/bizCiGateContract.js +131 -7
  62. package/src/utils/connectorContract.js +97 -7
  63. package/src/utils/cookbookFormat.js +81 -40
  64. package/src/utils/deployContract.js +140 -9
  65. package/src/utils/envContract.js +57 -1
  66. package/src/utils/handlerRef.js +181 -0
  67. package/src/utils/installContract.js +287 -41
  68. package/src/utils/libCompat.js +29 -7
  69. package/src/utils/migrationOrder.js +163 -0
  70. package/src/utils/preValidation.js +20 -7
  71. package/src/utils/setupDatabase.js +194 -13
  72. package/src/utils/testCoverageContract.js +539 -0
  73. package/src/utils/testNamespace.js +247 -23
  74. package/src/utils/throwawaySchema.js +207 -0
  75. package/src/validators/ServiceStructureValidator.js +2 -1
  76. package/templates/business-service/.dockerignore +42 -0
  77. package/templates/business-service/.gitlab-ci.yml +290 -0
  78. package/templates/business-service/Dockerfile +27 -0
  79. package/templates/business-service/README.md +213 -0
  80. package/templates/business-service/config/biz-docs-lint.tree.json +10 -0
  81. package/templates/business-service/config/env-templates/__SERVICE_NAME__.env +4 -0
  82. package/templates/business-service/config/env-templates/shared.env +65 -0
  83. package/templates/business-service/config/service/config.json +14 -0
  84. package/templates/business-service/config/service/integration-contract.json +12 -0
  85. package/templates/business-service/config/service/operations.json +41 -0
  86. package/templates/business-service/docker-compose.production.yml +60 -0
  87. package/templates/business-service/docker-compose.yml +93 -0
  88. package/templates/business-service/docs/80-setup/INSTALL.md +101 -0
  89. package/templates/business-service/docs/80-setup/PLATFORM_MATRIX.md +65 -0
  90. package/templates/business-service/docs/80-setup/README.md +18 -0
  91. package/templates/business-service/docs/80-setup/VALIDATION.md +78 -0
  92. package/templates/business-service/docs/README.md +18 -0
  93. package/templates/business-service/gitignore +42 -0
  94. package/templates/business-service/index.js +10 -0
  95. package/templates/business-service/init.sh +54 -0
  96. package/templates/business-service/jest.config.js +6 -0
  97. package/templates/business-service/package.json.template +31 -0
  98. package/templates/business-service/scripts/verify-deploy-uniform.sh +180 -0
  99. package/templates/business-service/src/handlers/v3/echo.js +39 -0
  100. package/templates/business-service/tests/cookbooks/echo.json +36 -0
  101. package/templates/business-service/tests/unit/handler.test.js +78 -0
  102. package/src/WorkflowTestRunner.js +0 -402
@@ -0,0 +1,41 @@
1
+ {
2
+ "schema_version": "3.0",
3
+ "operations": {
4
+ "echo": {
5
+ "description": "Return the submitted text unchanged. The example operation a new service replaces with its own.",
6
+ "handler": "handlers/v3/echo#echo",
7
+ "mutates": false,
8
+ "resource_type": null,
9
+ "bundle_scope": "platform",
10
+ "minRole": "EDITOR",
11
+ "input": {
12
+ "type": "object",
13
+ "additionalProperties": false,
14
+ "required": ["text"],
15
+ "properties": {
16
+ "text": {
17
+ "type": "string",
18
+ "description": "Text to return unchanged",
19
+ "minLength": 1,
20
+ "maxLength": 1000,
21
+ "examples": ["cookbook"]
22
+ }
23
+ }
24
+ },
25
+ "output": {
26
+ "type": "object",
27
+ "required": ["text", "length"],
28
+ "properties": {
29
+ "text": {
30
+ "type": "string",
31
+ "description": "The submitted text, byte for byte"
32
+ },
33
+ "length": {
34
+ "type": "integer",
35
+ "description": "Number of UTF-16 code units in the submitted text"
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,60 @@
1
+ # Production compose for __SERVICE_NAME__
2
+ #
3
+ # The image is the one CI built and pushed, pinned by its DIGEST (requirement R1,
4
+ # api/docs/operations/biz-rework-deployment-requirements.md). The build job
5
+ # publishes BIZ_IMAGE_DIGEST as a dotenv artifact and the deploy job exports it;
6
+ # with the value missing, the ${VAR:?…} marker fails the run by name instead of
7
+ # resolving to a nameless image. A mutable tag is refused because two concurrent
8
+ # pipelines make it nondeterministic between the push and the pull, and a
9
+ # rollback then has no target to roll back to.
10
+ #
11
+ # HOST CONTRACT OF THE BIZ BOX — owner decision
12
+ # api/docs/governance/confirmations/server-topology.md 004:
13
+ # * ../shared.env is the ONE host-level env file, at
14
+ # /var/www/html/oadrive/api_biz/shared.env. Its values are the infra box's
15
+ # property, so it is RENDERED there from that box's own env-active by
16
+ # api/scripts/provision/render-biz-shared-env.sh and carried to the biz box by
17
+ # the operator (server-topology 004 and 005) — role-biz writes no env file.
18
+ # This compose REFERENCES it and never copies it: one file per box, not one
19
+ # copy per repository.
20
+ # * ./config/env-active/__SERVICE_NAME__.env carries only this service's own
21
+ # keys.
22
+ # * biz_network is external and created by role-biz. It is the ONLY network
23
+ # that box has: the infra box's networks do not exist there, so naming one
24
+ # of them here would make the service undeployable — which is why the gate
25
+ # and tests assert that no other network name appears in this file at all.
26
+ #
27
+ # Usage: BIZ_IMAGE_DIGEST=sha256:… docker compose -f docker-compose.production.yml up -d
28
+
29
+ services:
30
+ __CONTAINER_NAME__:
31
+ image: "registry.gitlab.com/onlineapps/oadrive/__REPO_NAME__@${BIZ_IMAGE_DIGEST:?BIZ_IMAGE_DIGEST is required - the CI deploy job must export the digest of the pushed image}"
32
+ # There is no build section on purpose: docker refuses a build tag that
33
+ # carries a digest, and an image built here would in any case not be the one
34
+ # CI proved. Build locally through docker-compose.yml instead.
35
+ container_name: __CONTAINER_NAME__
36
+ deploy:
37
+ resources:
38
+ limits:
39
+ # Same norm as the dev compose, and deliberately the same number —
40
+ # owner decision api/docs/governance/confirmations/biz-memory-limits.md
41
+ # 001 names dev and production together, so production is not the
42
+ # place to discover a different limit.
43
+ #
44
+ # WHAT IT COVERS: the resident service process. There is no test
45
+ # runner here — suites do not run in production — so nothing else
46
+ # shares this cgroup by construction.
47
+ #
48
+ # This file used to declare no limit at all, which meant a new service
49
+ # reached production unbounded while its dev container was capped.
50
+ memory: 512M
51
+ env_file:
52
+ - ../shared.env
53
+ - ./config/env-active/__SERVICE_NAME__.env
54
+ restart: unless-stopped
55
+ networks:
56
+ - biz_network
57
+
58
+ networks:
59
+ biz_network:
60
+ external: true
@@ -0,0 +1,93 @@
1
+ services:
2
+ __CONTAINER_NAME__:
3
+ build:
4
+ context: .
5
+ container_name: __CONTAINER_NAME__
6
+ user: "1000:1000"
7
+ # No `ports:`. A biz service binds nothing — work arrives over MQ and is
8
+ # dispatched in-process (ADR 0005), and
9
+ # docs/biz/00-model/service-shape.md § No-HTTP tests states the rule for the
10
+ # compose file directly. The scaffold used to publish a port here, so every
11
+ # new service broke the shape at the moment it was created.
12
+ deploy:
13
+ resources:
14
+ limits:
15
+ # The platform norm for a biz service in operation — owner decision
16
+ # api/docs/governance/confirmations/biz-memory-limits.md 001, which
17
+ # holds in dev AND in production. It is a prescribed value, not one
18
+ # measured on this service or on any other.
19
+ #
20
+ # WHAT IT COVERS: the resident service process, and nothing else. No
21
+ # test suite may share this cgroup — that is why the runner below has
22
+ # a budget of its own, and why raising this number is never the fix
23
+ # for a suite that runs out of memory.
24
+ #
25
+ # Revise it when this service exceeds 60% of the limit in operation,
26
+ # measured the way the norm was: cgroup memory.stat:anon, sampled
27
+ # inside the running container.
28
+ memory: 512M
29
+ volumes:
30
+ - .:/app
31
+ env_file:
32
+ - ./config/env-active/shared.env
33
+ - ./config/env-active/__SERVICE_NAME__.env
34
+ networks:
35
+ - api_network
36
+ restart: unless-stopped
37
+ entrypoint: ["/bin/sh", "init.sh"]
38
+ # PID 1 is node, never npm: init.sh execs what it is given, and npm does
39
+ # not forward SIGTERM - a stop would be ten seconds of waiting and then a
40
+ # SIGKILL through a service that never closed a channel. Row R-PID1,
41
+ # confirmation biz-compose-pid1 001/002.
42
+ command: ["node", "index.js"]
43
+
44
+ # --- oa-test-runner v1
45
+ # One-shot test runner — part of the unified biz service shape
46
+ # (owner decision 2026-09-07, docs/governance/confirmations/biz-test-container.md 001).
47
+ #
48
+ # Same build, same env_file, same networks as the service above, so
49
+ # "tests run in a container" stays true — but its OWN memory budget.
50
+ #
51
+ # WHY: two loads were sharing one budget, not one load being too big. Measured
52
+ # by the BIZ-META thread on 2026-09-06/07 inside the containers, from
53
+ # /sys/fs/cgroup/memory.peak (api_biz/meta/docker-compose.yml carries the full
54
+ # numbers): the unit suite peaked at 343 MiB and the service's own peak was
55
+ # 132 MiB, against a shared 384 MiB limit — so a `docker exec` test run was
56
+ # killed by the kernel AND took the service down with it, while the
57
+ # integration suite failed intermittently on a different file each time.
58
+ #
59
+ # The 1g below is the platform NORM for the runner — owner decision
60
+ # api/docs/governance/confirmations/biz-memory-limits.md 001, the same
61
+ # decision that sets the service's 512M above. It applies in dev and CI only,
62
+ # and it assumes one suite at a time (`--maxWorkers=1`). It is no longer
63
+ # "meta's number carried over": measurement is what produced the norm, the
64
+ # norm is what this file now states.
65
+ #
66
+ # `mem_limit`, not the `deploy.resources.limits` form: both were measured to
67
+ # apply on `docker compose run` with Compose v5.1.3, and this is the plain
68
+ # Compose one rather than the swarm section Compose honours in part.
69
+ #
70
+ # NOT started by a plain `docker compose up` — the `test` profile keeps it
71
+ # out. No `restart`, no healthcheck: it runs once and exits. The one
72
+ # documented way to run it is the npm scripts in package.json, which wrap:
73
+ # docker compose run --rm __CONTAINER_NAME___tests npm run test:unit
74
+ __CONTAINER_NAME___tests:
75
+ build:
76
+ context: .
77
+ profiles:
78
+ - test
79
+ user: "1000:1000"
80
+ mem_limit: 1g
81
+ volumes:
82
+ - .:/app
83
+ env_file:
84
+ - ./config/env-active/shared.env
85
+ - ./config/env-active/__SERVICE_NAME__.env
86
+ networks:
87
+ - api_network
88
+ entrypoint: ["/bin/sh", "init.sh"]
89
+ # --- end oa-test-runner v1
90
+
91
+ networks:
92
+ api_network:
93
+ external: true
@@ -0,0 +1,101 @@
1
+ Parent: [../../README.md](../../README.md)
2
+ Owns: the installation flow for a new __REPO_NAME__ instance — prerequisites, environment preparation, the bootstrap sequence, and what this service does NOT need.
3
+
4
+ # INSTALL
5
+
6
+ ## Scope
7
+
8
+ Bootstraps `__REPO_NAME__` — a business service with no database and no object
9
+ storage. It binds no port: work arrives over MQ and is dispatched in-process
10
+ (ADR 0005, `api/docs/biz/80-decisions/0005-no-http-in-biz-containers.md`), so
11
+ there is no HTTP endpoint to install or to call afterwards.
12
+
13
+ What the service needs is declared once, in
14
+ `config/service/integration-contract.json` → `requiredConnectors`: `mq` only.
15
+ `db`, `redis` and `minio` are `false`, which is why this document has no SQL
16
+ step — the installation contract derives the SQL half from the contract's
17
+ `database` block, and this service declares none.
18
+
19
+ ## Prerequisites
20
+
21
+ - Docker and Docker Compose v2.
22
+ - Node.js — the major this repository declares in `package.json` `engines.node`.
23
+ The same major is in the `Dockerfile` base image and in `.gitlab-ci.yml`;
24
+ requirement R3 of the deploy contract fails the gate if the three disagree, so
25
+ read the major there rather than from a number written here.
26
+ - A reachable RabbitMQ and service registry. Their URLs arrive as
27
+ `${RABBITMQ_URL}` and `${REGISTRY_URL}`, the two placeholders
28
+ `config/service/config.json` carries; ConfigLoader substitutes them at boot and
29
+ fails by name if either is unset.
30
+ - The external Docker network the compose file names (`api_network` in dev), and
31
+ on the production biz box `biz_network` — both are created outside this
32
+ repository.
33
+
34
+ ## Environment preparation
35
+
36
+ ```bash
37
+ mkdir -p config/env-active
38
+ cp config/env-templates/shared.env config/env-active/shared.env
39
+ cp config/env-templates/__SERVICE_NAME__.env config/env-active/__SERVICE_NAME__.env
40
+ ```
41
+
42
+ Then edit both files: `shared.env` carries the infrastructure URLs and the
43
+ testing namespace (`TESTING_TENANT_ID`, `TESTING_WORKSPACE_ID`), the service file
44
+ carries what belongs to this service alone. `config/env-active/` is gitignored —
45
+ the templates are the versioned half, the active files are per-instance.
46
+
47
+ Every variable a handler reads must additionally be declared in
48
+ `config/service/integration-contract.json` → `env`, with a reason per name. The
49
+ scaffold declares no `env` block because it reads none: the only two names it
50
+ needs are the `${…}` placeholders above, and the contract must not repeat what a
51
+ config placeholder already covers. The first handler that reads an environment
52
+ variable adds the block.
53
+
54
+ On the production biz box the second env file is a host-level `../shared.env`,
55
+ written once per box and referenced rather than copied (owner decision
56
+ `api/docs/governance/confirmations/server-topology.md` 004).
57
+
58
+ ## Bootstrap sequence
59
+
60
+ 1. Prepare the env files (above).
61
+ 2. Start the dependencies this service declares required — MQ and the registry.
62
+ They are not part of this repository.
63
+ 3. Start the service:
64
+
65
+ ```bash
66
+ docker compose up -d --build
67
+ ```
68
+
69
+ `init.sh` installs dependencies on first start and skips the install when
70
+ neither `package.json` nor `package-lock.json` changed.
71
+
72
+ That command builds the SERVICE only. The one-shot test runner carries
73
+ `profiles: [test]`, so it is outside a plain build as well as outside a plain
74
+ `up` — measured 2026-09-14: `docker compose build --dry-run` builds one image,
75
+ `docker compose --profile test build --dry-run` builds two. Build it when the
76
+ suites are to run against this checkout:
77
+
78
+ ```bash
79
+ docker compose --profile test build
80
+ ```
81
+ 4. Verify the install — [VALIDATION.md](VALIDATION.md) states the checks and what
82
+ each one must print.
83
+
84
+ ## Production
85
+
86
+ The production compose pins the image by digest, so it is pulled, never built:
87
+
88
+ ```bash
89
+ BIZ_IMAGE_DIGEST=sha256:… docker compose -f docker-compose.production.yml pull
90
+ BIZ_IMAGE_DIGEST=sha256:… docker compose -f docker-compose.production.yml up -d
91
+ ```
92
+
93
+ CI publishes that digest from the build stage and the deploy job refuses to run
94
+ without it (requirement R1). A first deploy also clones the checkout on the box
95
+ if it is missing, so onboarding a new service needs no manual step there.
96
+
97
+ ## Status
98
+
99
+ Current. One of the three files the installation contract requires by name —
100
+ `biz-ci-gate verify-install-contract` (and `verify-contract`, which folds it in)
101
+ fails when it is missing.
@@ -0,0 +1,65 @@
1
+ Parent: [../../README.md](../../README.md)
2
+ Owns: the execution differences between Linux, macOS and a CI/cloud runner when installing or validating __REPO_NAME__.
3
+
4
+ # PLATFORM MATRIX
5
+
6
+ Every command in this document runs from the repository root
7
+ (`api_biz/__SERVICE_NAME__/`). Relative paths in prose and in links resolve from
8
+ this document's own directory (`docs/80-setup/`) instead — the two bases differ,
9
+ so a path copied from one into the other does not resolve.
10
+
11
+ The service itself runs inside the image the `Dockerfile` builds, so the host
12
+ platform decides how the containers are reached, never what runs in them. That
13
+ is why the differences below are about the shell around the container, not about
14
+ the service.
15
+
16
+ ## Linux
17
+
18
+ - Reference environment.
19
+ - The compose file runs the service as `user: "1000:1000"`. On a host whose own
20
+ uid is not 1000, files the container writes into the bind mount (`node_modules`,
21
+ `conn-runtime/`) belong to that uid — run the container as your own uid, or
22
+ accept the ownership.
23
+ - Start and validate:
24
+
25
+ ```bash
26
+ docker compose up -d --build
27
+ npm run test:container
28
+ npm run test:cookbooks:container
29
+ ```
30
+
31
+ ## macOS
32
+
33
+ - Same commands; Docker Desktop maps the bind mount, so the `user: "1000:1000"`
34
+ line above has no visible effect on file ownership.
35
+ - Run the suites through the container scripts above rather than on the host: the
36
+ runner has the env files and the same image the service uses. A bare `npm test`
37
+ on the host runs against whatever Node and environment the shell happens to
38
+ carry.
39
+ - The dependency guard in `init.sh` uses `md5sum`, which runs inside the Alpine
40
+ container, not on the host — macOS shipping `md5` instead is irrelevant here.
41
+
42
+ ## CI / cloud runner
43
+
44
+ - Non-interactive: no prompts, and nothing may depend on a developer's shell.
45
+ `.gitlab-ci.yml` installs with `npm ci` and runs `npm run test:ci`.
46
+ - The contract gates run from the installed validator, not from a checkout of the
47
+ platform repository:
48
+
49
+ ```bash
50
+ node node_modules/@onlineapps/conn-orch-validator/src/cli/biz-ci-gate.js verify-contract
51
+ ```
52
+
53
+ `verify-contract` needs `LIBRARIES_SSOT_URL` (requirement R6 compares the
54
+ `@onlineapps/*` pins against the platform library set) and the service's
55
+ dependencies installed — the test-coverage half asks the service's own jest
56
+ which files it would run.
57
+ - The build stage publishes the image digest as a dotenv artifact and the deploy
58
+ stage refuses to run without it; both stages are declared in `.gitlab-ci.yml`.
59
+ - Windows is outside the supported matrix.
60
+
61
+ ## Status
62
+
63
+ Current. One of the three files the installation contract requires by name —
64
+ `biz-ci-gate verify-install-contract` (and `verify-contract`, which folds it in)
65
+ fails when it is missing.
@@ -0,0 +1,18 @@
1
+ Parent: [../README.md](../README.md)
2
+ Owns: the map of the installation branch — which of the three installation documents answers which question
3
+
4
+ # Setup
5
+
6
+ An operator installs this repository from these three documents, in this order.
7
+
8
+ - [INSTALL](INSTALL.md) — prerequisites, environment preparation and the bootstrap sequence
9
+ - [PLATFORM_MATRIX](PLATFORM_MATRIX.md) — what differs between Linux, macOS and a CI runner
10
+ - [VALIDATION](VALIDATION.md) — the checks that prove the install, and what each failure means
11
+
12
+ The three are the platform installation contract (`SETUP_DOCS`), so the set is
13
+ fixed: a repository carries all three or it does not satisfy the contract.
14
+
15
+ ## Status
16
+
17
+ Current. The map of this branch: `M001` of the documentation lint reports a leaf
18
+ this file does not link, and `S008` reports a branch that carries no map at all.
@@ -0,0 +1,78 @@
1
+ Parent: [../../README.md](../../README.md)
2
+ Owns: the mandatory checks after installing __REPO_NAME__, the output each one must produce, and the failure signature that says which check did not hold.
3
+
4
+ # VALIDATION
5
+
6
+ ## Required checks after install
7
+
8
+ 1. The repository satisfies the platform contract — deploy, installation,
9
+ environment and test coverage.
10
+ 2. The unit suite passes in the test runner container.
11
+ 3. The cookbook run dispatches the example operation and asserts its output by
12
+ value.
13
+ 4. The container reaches the end of boot and registers. **There is no health
14
+ endpoint to call** — a biz container serves no HTTP (ADR 0005), so the
15
+ container log and the registry are the only readiness evidence.
16
+
17
+ ## Command sequence
18
+
19
+ Every command runs from the repository root (`api_biz/__SERVICE_NAME__/`).
20
+
21
+ ```bash
22
+ # 1 — contract gates (deploy R1-R5/R7-R8, installation docs, env, test coverage, R6)
23
+ node node_modules/@onlineapps/conn-orch-validator/src/cli/biz-ci-gate.js verify-contract
24
+
25
+ # 2 — unit suite, in the one-shot runner beside the service
26
+ npm run test:container
27
+
28
+ # 3 — cookbooks, in the same runner (it has the env files)
29
+ npm run test:cookbooks:container
30
+
31
+ # 4 — boot evidence
32
+ docker compose logs --tail=50 __CONTAINER_NAME__
33
+ ```
34
+
35
+ ## Expected outcome
36
+
37
+ | Check | Passes when |
38
+ |---|---|
39
+ | 1 contract gates | the run ends with `[BizCiGate] OK verify-contract` and exits `0`; each half prints its own `OK` line first |
40
+ | 2 unit suite | jest reports no failing test and the command exits `0` |
41
+ | 3 cookbooks | the run prints `Failed: 0`, ends with `=== Pre-Validation Completed Successfully ===` and writes `conn-runtime/validation-proof.json` |
42
+ | 4 boot | the log shows registration and handler validation, and the container stays up (`docker compose ps` reports it running, not restarting) |
43
+
44
+ The cookbook check asserts a VALUE, not that something came back: the shipped
45
+ cookbook requires the echoed text to equal what it sent and its length to equal
46
+ `8`. Keep that property when the example operation is replaced — an assertion of
47
+ truthiness proves the dispatch worked and nothing about the result
48
+ (`.claude/rules/architecture-principles.md` §10a).
49
+
50
+ ## Failure signatures
51
+
52
+ - `[BizCiGate] Missing file - …/config/service/integration-contract.json` — the
53
+ contract is gone; every gate reads it first.
54
+ - `[BizCiGate] FAIL … SETUP_DOCS — Missing required file - docs/80-setup/…` — one
55
+ of these three documents was deleted or renamed.
56
+ - `[BizCiGate] FAIL … ENV_COMPLETENESS — "X" is read in … but is neither declared
57
+ … nor covered` — a handler started reading an environment variable; declare it
58
+ in the contract's `env` block with the reason, or delete the read.
59
+ - `[BizCiGate] FAIL … TEST_RUNS_NOWHERE — Test file runs nowhere - …` — a test
60
+ file exists that the `test:all` chain does not run; put it in the chain, or
61
+ declare its script in `stackTiers` with the reason it cannot be there.
62
+ - `[BizCiGate] Missing package script - test:all …` — the chain the coverage gate
63
+ measures against was removed.
64
+ - `[SetupDatabase] Missing database.collation - the schema is created with the
65
+ collation the service declares, and there is no default.` — `setup-db` was asked
66
+ to build a schema for a service whose contract carries a `database` block
67
+ without `collation`. Add the key with the collation this service's tables
68
+ already use; the same key the production runbook reads, and the one uniform row
69
+ `D-DB-COLLATION` asks for.
70
+ - `[ConfigLoader] Missing environment variable - X is required (config placeholder
71
+ ${X})` at boot — the env-active files were not prepared, or that name is missing
72
+ from them.
73
+
74
+ ## Status
75
+
76
+ Current. One of the three files the installation contract requires by name —
77
+ `biz-ci-gate verify-install-contract` (and `verify-contract`, which folds it in)
78
+ fails when it is missing.
@@ -0,0 +1,18 @@
1
+ Parent: [../README.md](../README.md)
2
+ Owns: the map of this repository's documentation tree — which branch answers which question
3
+
4
+ # __REPO_NAME__ — documentation
5
+
6
+ Every document in this tree is reached from here by clicking down; a node no map
7
+ links to is a node nobody finds (DOC-STANDARD rule 1).
8
+
9
+ - [Setup](80-setup/README.md) — installing this repository and proving the install
10
+
11
+ The platform-wide concepts this service is built on — the service shape, the
12
+ tenancy model, the contracts it speaks — live in the platform tree
13
+ `api/docs/biz/`, and this tree links to them rather than restating them.
14
+
15
+ ## Status
16
+
17
+ Current. The map of this tree: `L006` of the documentation lint reports every node
18
+ this file does not reach by clicking down.
@@ -0,0 +1,42 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # Runtime data
5
+ config/runtime/
6
+ config/env-active/
7
+
8
+ # Validation proof written by `npm run test:cookbooks` (the validator's own CLI)
9
+ # and by the wrapper on every boot — generated, never committed (same line
10
+ # api_biz/hello-service carries).
11
+ conn-runtime/
12
+
13
+ # Deployability signal written by the manifest step of the boot validation and
14
+ # by `npx oa-validate` (`ci/deployability.json`, the shape of
15
+ # `ci/integration-signal.json`) - generated on every start, never committed.
16
+ ci/
17
+
18
+ # Logs
19
+ logs/
20
+ *.log
21
+ npm-debug.log*
22
+
23
+ # Coverage
24
+ coverage/
25
+
26
+ # Environment (legacy)
27
+ .env
28
+ .env.local
29
+
30
+ # IDE
31
+ .vscode/
32
+ .idea/
33
+ *.swp
34
+ *.swo
35
+ *~
36
+
37
+ # OS
38
+ .DS_Store
39
+ Thumbs.db
40
+
41
+ # Build artifacts
42
+ .oa_drive_deps_hash
@@ -0,0 +1,10 @@
1
+ /**
2
+ * __SERVICE_NAME__ - Main Entry Point
3
+ * Uses unified bootstrap from @onlineapps/service-wrapper
4
+ */
5
+ const { bootstrap } = require('@onlineapps/service-wrapper');
6
+
7
+ bootstrap(__dirname).catch(error => {
8
+ console.error('Failed to start service:', error);
9
+ process.exit(1);
10
+ });
@@ -0,0 +1,54 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ oa_npm_install() {
5
+ npm install --no-audit --no-fund
6
+ }
7
+
8
+ # --- oa-deps-guard v1 (identical in every OA Drive init.sh) ---
9
+ # The dev stack's only skip condition: the md5 of package.json + package-lock.json
10
+ # against the marker the last SUCCESSFUL install wrote. A package-directory probe
11
+ # cannot see a version bump and an mtime probe cannot see a checkout that rewinds
12
+ # package.json, so neither is used here or anywhere else in the file. Enforced byte
13
+ # for byte, within this repo, by tests/scripts/infra-init-scripts.bats — across the
14
+ # eight infra/*/init.sh, this template and the generator's output. A copy pasted
15
+ # into an api_biz/* repo is kept in sync by review only; the gate cannot reach
16
+ # another checkout. A service's own install steps belong in oa_npm_install() above,
17
+ # everything else it needs goes below.
18
+ MARKER_FILE=".oa_drive_deps_hash"
19
+
20
+ HASH_INPUT=""
21
+ if [ -f "package.json" ]; then
22
+ HASH_INPUT="${HASH_INPUT}$(md5sum package.json | awk '{print $1}')"
23
+ fi
24
+ if [ -f "package-lock.json" ]; then
25
+ HASH_INPUT="${HASH_INPUT}$(md5sum package-lock.json | awk '{print $1}')"
26
+ fi
27
+ CURRENT_HASH=$(echo "$HASH_INPUT" | md5sum | awk '{print $1}')
28
+
29
+ PREVIOUS_HASH=""
30
+ if [ -f "$MARKER_FILE" ]; then
31
+ PREVIOUS_HASH=$(cat "$MARKER_FILE" 2>/dev/null || true)
32
+ fi
33
+
34
+ NEEDS_INSTALL=0
35
+ if [ ! -d "node_modules" ] || [ -z "$(ls -A node_modules 2>/dev/null)" ]; then
36
+ NEEDS_INSTALL=1
37
+ fi
38
+
39
+ if [ "$CURRENT_HASH" != "$PREVIOUS_HASH" ] || [ "$NEEDS_INSTALL" = "1" ]; then
40
+ echo "[init.sh] Installing dependencies (hash changed or node_modules missing)..."
41
+ if oa_npm_install; then
42
+ echo "$CURRENT_HASH" > "$MARKER_FILE"
43
+ echo "[init.sh] Dependencies installed, marker $MARKER_FILE updated."
44
+ else
45
+ echo "[init.sh] Dependency install failed - marker $MARKER_FILE left untouched, so the next start retries instead of trusting a half-installed tree. Fix: read the npm error above, then run 'npm install' in this service directory."
46
+ exit 1
47
+ fi
48
+ else
49
+ echo "[init.sh] Dependencies unchanged. Skipping npm install."
50
+ fi
51
+ # --- end oa-deps-guard v1 ---
52
+
53
+ # Run the passed command (e.g. npm run dev or npm run prod).
54
+ exec "$@"
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ testEnvironment: 'node',
3
+ testMatch: ['**/tests/**/*.test.js'],
4
+ testTimeout: 30000,
5
+ maxWorkers: 1
6
+ };
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "__REGISTRY_NAME__",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "main": "index.js",
6
+ "engines": {
7
+ "node": ">=24.0.0 <25"
8
+ },
9
+ "scripts": {
10
+ "start": "node index.js",
11
+ "dev": "nodemon index.js",
12
+ "test": "jest --config jest.config.js tests/unit --runInBand",
13
+ "test:ci": "jest",
14
+ "test:unit": "jest tests/unit",
15
+ "test:all": "npm run test:unit",
16
+ "test:container": "docker compose run --rm __CONTAINER_NAME___tests npm run test:unit",
17
+ "test:all:container": "docker compose run --rm __CONTAINER_NAME___tests npm run test:all",
18
+ "test:coverage": "jest --coverage",
19
+ "test:cookbooks": "node node_modules/@onlineapps/conn-orch-validator/src/cli/biz-ci-gate.js run-prevalidation",
20
+ "test:cookbooks:container": "docker compose run --rm __CONTAINER_NAME___tests npm run test:cookbooks"
21
+ },
22
+ "dependencies": {
23
+ "@onlineapps/conn-orch-validator": "__SSOT_PIN__",
24
+ "@onlineapps/service-wrapper": "__SSOT_PIN__",
25
+ "@onlineapps/service-common": "__SSOT_PIN__"
26
+ },
27
+ "devDependencies": {
28
+ "jest": "29.7.0",
29
+ "nodemon": "3.1.7"
30
+ }
31
+ }