@gate-forge/pack-task 0.7.1 → 0.8.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 (2) hide show
  1. package/README.md +80 -0
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -2,6 +2,86 @@
2
2
 
3
3
  Background-task discovery pack: a pure-TypeScript GPP/3 in-process detector that finds background-task signatures in `.ts`/`.js`/`.mjs` source — no Python subprocess, no execution, no external deps — plus an audit-trail entity-adapter schema and an example server that proves the five obligation contracts the pack claims.
4
4
 
5
+ ## Engine-level proof: the queue observer
6
+
7
+ A background job's attempts, terminal state and idempotency live in the
8
+ QUEUE, so the engine reads the queue itself. A test's own "the job
9
+ succeeded" is never proof — exactly like a test's own HTTP status. A
10
+ repository opts in with an engine-owned `queueObserver` block in
11
+ `.gateforge.yml`:
12
+
13
+ ```yaml
14
+ queueObserver:
15
+ kind: bullmq # or a module path whose default export is a factory
16
+ connection:
17
+ urlEnv: GATEFORGE_QUEUE_REDIS_URL # host/port form: {host, port}
18
+ queues:
19
+ - name: mailer # the queue the engine reads
20
+ taskResourceId: task.email.send
21
+ pollIntervalMs: 100 # transition-sampling interval (default 200)
22
+ terminalTimeoutMs: 30000 # bound on waiting for the delivery to settle
23
+ ```
24
+
25
+ - The block lives in `.gateforge.yml`, so it is inside the trusted policy
26
+ digest: a candidate cannot point the engine at a queue it controls.
27
+ - Connection material never carries a secret inline — the URL form names
28
+ an environment variable the WITNESS process resolves privately; the
29
+ value is never sealed into a record, a report or argv.
30
+ - `kind: bullmq` reads the queue with the project's own `bullmq` and
31
+ `ioredis` (optional peers of `@gate-forge/witness`: install them next
32
+ to the app, `npm install --save-dev bullmq ioredis`). A project that
33
+ declares no BullMQ observer never installs or loads them; one that
34
+ declares it without them fails closed with that install command.
35
+ - WITHOUT the block the `task` namespace is **unavailable** (every
36
+ `task:*` contract fails closed, cause `VERIFIER_UNSUPPORTED`) and every
37
+ `engine-task` case blocks with a naming diagnostic. `gateforge init`
38
+ therefore recommends this pack's cases only when the block is
39
+ configured.
40
+ - `test-gates` hands the declaration to the witness it spawns
41
+ (`GATEFORGE_QUEUE_OBSERVER` + `GATEFORGE_QUEUE_OBSERVER_CONFIG`); a
42
+ repository without the block passes nothing and its spawn environment
43
+ is byte-identical.
44
+
45
+ ### What a `deliver` case does
46
+
47
+ The engine produces the delivery, not the suite: it stamps the delivery
48
+ identity, the idempotency key and the attempt bound (taken from the
49
+ case's own `attempts` rule, so the policy declares the retry bound
50
+ exactly once), then polls the queue until every produced job settles and
51
+ seals what it read. `count` deliveries of one case share ONE idempotency
52
+ key — that is what makes a duplicate-key case a duplicate — while each
53
+ delivery gets its own identity.
54
+
55
+ ### What an `attempts` rule settles
56
+
57
+ | Field | Meaning |
58
+ | --- | --- |
59
+ | `resourceId` | the task resource the case delivers to (must match the action) |
60
+ | `count` | the declared attempt bound: no job may exceed it, and the queue's own declared bound may not exceed it |
61
+ | `terminal` | `succeeded` (every job `completed`), `failed` (every job `failed`), `rejected` (every job failed on its FIRST attempt) |
62
+ | `minAttempts` (optional) | every job used at least this many attempts — "retries up to N" cannot be satisfied by a queue that never retried |
63
+ | `recoveredFromStall` (optional) | the engine's own timeline must show a lost-worker reclaim: the same job handed out again with UNCHANGED attempts and no failure reason (an error retry raises both) — and the delivery still settles |
64
+
65
+ A delivery case that declares no `attempts` rule settles nothing and is
66
+ blocked (`BEHAVIOR_BINDING_MISMATCH`), and a case that carries HTTP
67
+ attempts or a browser observation is rejected outright: those prove a
68
+ transport claim, not a background job.
69
+
70
+ ### Proving it
71
+
72
+ `packages/cli/test/task-queue-behavior-e2e.test.ts` runs the real CLI,
73
+ the real witness and a REAL BullMQ application (`example/task-bullmq/`:
74
+ a queue plus worker processes, one of which is genuinely lost mid-job)
75
+ against a disposable Redis. Each contract ships a fail variant the engine
76
+ must block. The suite SKIPS unless `GATEFORGE_TEST_REDIS_URL` names a
77
+ live Redis — the engine's queue reader needs a real queue, and a fake one
78
+ would be a mock as proof:
79
+
80
+ ```sh
81
+ docker run -d --rm --name gateforge-queue-redis -p 127.0.0.1:6379 redis:7-alpine
82
+ GATEFORGE_TEST_REDIS_URL=redis://127.0.0.1:6379 npm test -- task-queue-behavior-e2e
83
+ ```
84
+
5
85
  ## How discovery works
6
86
 
7
87
  The detector scans `.ts`/`.tsx`/`.js`/`.mjs` files with regex-based AST-light patterns (matching `pack-auth`'s strategy). It recognizes the task constructs below and reports its verdict through the discovery outcome's typed blocking vocabulary — the pack emits no resources and no classification signals of its own:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gate-forge/pack-task",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Background-task discovery pack: TS-only AST-light detector (GPP/3) for BullMQ / Bee-Queue / custom queues / message handlers / recurring jobs / @Task @Queue decorators, idempotency + retry-policy + terminal-on + observability contracts, audit-run entity-adapter schema.",
6
6
  "type": "module",
@@ -26,8 +26,8 @@
26
26
  "test": "vitest run"
27
27
  },
28
28
  "dependencies": {
29
- "@gate-forge/core": "^0.7.1",
30
- "@gate-forge/plugin-protocol": "^0.7.1"
29
+ "@gate-forge/core": "^0.8.0",
30
+ "@gate-forge/plugin-protocol": "^0.8.0"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"