@hasna/loops 0.4.28 → 0.4.29

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 (77) hide show
  1. package/CHANGELOG.md +84 -1
  2. package/README.md +226 -49
  3. package/dist/api/index.d.ts +20 -19
  4. package/dist/api/index.js +6846 -1087
  5. package/dist/cli/index.js +2471 -885
  6. package/dist/cli/safe-error-context.d.ts +1 -0
  7. package/dist/daemon/daemon.d.ts +1 -0
  8. package/dist/daemon/index.js +1786 -493
  9. package/dist/generated/storage-kit/index.d.ts +1 -1
  10. package/dist/generated/storage-kit/mode.d.ts +6 -12
  11. package/dist/index.d.ts +3 -3
  12. package/dist/index.js +4778 -1016
  13. package/dist/lib/advancement.d.ts +52 -0
  14. package/dist/lib/agent-adapter.d.ts +20 -2
  15. package/dist/lib/auth/route-policy.d.ts +14 -0
  16. package/dist/lib/auth/tenant-auth.d.ts +38 -0
  17. package/dist/lib/cloud/mode.d.ts +2 -8
  18. package/dist/lib/cloud/storage.d.ts +1 -2
  19. package/dist/lib/cloud/transport.d.ts +4 -18
  20. package/dist/lib/errors.d.ts +43 -1
  21. package/dist/lib/format.d.ts +2 -2
  22. package/dist/lib/goal/runner.d.ts +36 -3
  23. package/dist/lib/health.d.ts +1 -1
  24. package/dist/lib/labels.d.ts +4 -0
  25. package/dist/lib/loop-status.d.ts +4 -0
  26. package/dist/lib/migration.d.ts +4 -17
  27. package/dist/lib/mode.d.ts +2 -5
  28. package/dist/lib/mode.js +27 -36
  29. package/dist/lib/route/index.d.ts +2 -2
  30. package/dist/lib/route/throttle.d.ts +7 -0
  31. package/dist/lib/route/types.d.ts +3 -0
  32. package/dist/lib/run-completion.d.ts +18 -0
  33. package/dist/lib/scheduler.d.ts +5 -11
  34. package/dist/lib/storage/contract.d.ts +15 -1
  35. package/dist/lib/storage/index.d.ts +1 -1
  36. package/dist/lib/storage/index.js +3864 -457
  37. package/dist/lib/storage/pg-executor.d.ts +10 -5
  38. package/dist/lib/storage/postgres-loop-storage.d.ts +50 -24
  39. package/dist/lib/storage/postgres-schema.d.ts +8 -0
  40. package/dist/lib/storage/postgres-schema.js +1323 -1
  41. package/dist/lib/storage/postgres.d.ts +3 -1
  42. package/dist/lib/storage/postgres.js +1353 -27
  43. package/dist/lib/storage/provider-credentials.d.ts +84 -0
  44. package/dist/lib/storage/shared-database-transfer.d.ts +136 -0
  45. package/dist/lib/storage/sqlite.d.ts +15 -2
  46. package/dist/lib/storage/sqlite.js +1299 -217
  47. package/dist/lib/storage/tenant-backfill-s3.d.ts +53 -0
  48. package/dist/lib/storage/tenant-backfill.d.ts +40 -0
  49. package/dist/lib/store/index.d.ts +11 -8
  50. package/dist/lib/store.d.ts +52 -7
  51. package/dist/lib/store.js +1266 -217
  52. package/dist/lib/templates.d.ts +11 -0
  53. package/dist/lib/workflow-events.d.ts +6 -0
  54. package/dist/lib/workflow-provenance.d.ts +8 -0
  55. package/dist/lib/workflow-runner.d.ts +52 -4
  56. package/dist/mcp/index.js +1961 -651
  57. package/dist/runner/index.d.ts +20 -2
  58. package/dist/runner/index.js +2113 -177
  59. package/dist/sdk/http.d.ts +211 -3
  60. package/dist/sdk/http.js +301 -0
  61. package/dist/sdk/index.d.ts +7 -2
  62. package/dist/sdk/index.js +1959 -711
  63. package/dist/serve/index.d.ts +14 -0
  64. package/dist/serve/index.js +12323 -1743
  65. package/dist/types.d.ts +64 -3
  66. package/docs/AUTOMATION_RUNTIME_DESIGN.md +32 -32
  67. package/docs/CUTOVER-RUNBOOK.md +158 -31
  68. package/docs/DEPLOYMENT_MODES.md +78 -56
  69. package/docs/RUNTIME_BOUNDARY.md +26 -26
  70. package/docs/SHARED-DATABASE-TRANSFER.md +95 -0
  71. package/docs/SHARED_KIT_EXTRACTION_INVENTORY.md +631 -0
  72. package/docs/TRANSCRIPT_LOOP_PATTERNS.md +3 -3
  73. package/docs/UNIFIED_PRODUCT_CONTRACT.md +365 -0
  74. package/docs/USAGE.md +143 -52
  75. package/docs/workflows/transcript-feedback-to-loops.json +2 -2
  76. package/package.json +7 -3
  77. package/dist/lib/storage/pg-runner-claim.d.ts +0 -40
@@ -0,0 +1,365 @@
1
+ # Unified Loops Product Contract
2
+
3
+ **Status:** Decision record; implementation target
4
+ **Scope:** One Loops application contract, its adapters, process roles, persistence choices, and compatibility path.
5
+
6
+ ## 1. Decision, scope, non-goals, and vocabulary
7
+
8
+ ### Decision
9
+
10
+ Loops is one product and one application contract. SQLite is its zero-configuration embedded-authority persistence; that embedded authority may be packaged or hosted without becoming the multi-tenant server role. PostgreSQL is the explicit persistence required by the network multi-tenant server role, including when that role runs on AWS. CLI, SDK, HTTP API, MCP, daemon/scheduler, runner, and server/admin are adapters or process roles over that one contract.
11
+
12
+ There is no target public `local`, `self_hosted`, or `cloud` product-mode enum. Those terms may describe a deployment topology conversationally, but must not select product behavior, authority, or storage semantics. Existing legacy mode values are temporary compatibility inputs only; they are not a target configuration model and must be normalized or rejected at the boundary.
13
+
14
+ ### Scope
15
+
16
+ This record defines:
17
+
18
+ - the target authority-selection and configuration precedence;
19
+ - adapter ownership over a shared `LoopsApplication` capability contract;
20
+ - canonical executable, SDK, OpenAPI, MCP, and compatibility direction;
21
+ - storage, tenancy, recovery, migration, restore, and cutover invariants;
22
+ - evidence gates required before parity or cutover claims.
23
+
24
+ ### Non-goals
25
+
26
+ This record does not:
27
+
28
+ - assert present parity between embedded and server deployments;
29
+ - assert live AWS readiness, a configured AWS environment, or an AWS rollout plan;
30
+ - assert a lossless SQLite-to-PostgreSQL migration is currently implemented;
31
+ - assert that there are no external consumers of the existing binaries or exports;
32
+ - introduce a dual-authority, cache-and-spool, offline write queue, or automatic replication product;
33
+ - prescribe credentials, endpoints, tenant identifiers, or environment-specific values.
34
+
35
+ ### Vocabulary
36
+
37
+ | Term | Meaning |
38
+ | --- | --- |
39
+ | **Application contract** | The canonical capability, domain, and invariant contract implemented once by `LoopsApplication`. |
40
+ | **Authority** | The source of truth a client invokes: embedded application authority or a remote HTTP application authority. |
41
+ | **Persistence** | The backing store selected by an authority process: embedded SQLite or explicitly configured PostgreSQL. |
42
+ | **Adapter** | A boundary translating CLI, HTTP, MCP, SDK, daemon, or runner inputs into application capabilities. |
43
+ | **Process role** | The capabilities deliberately enabled in a process, such as client, scheduler, worker, server, or admin. |
44
+ | **Topology** | Operational placement and connectivity, such as a laptop process, a service fleet, or an AWS deployment. |
45
+ | **Readiness** | Evidence-backed ability to run a chosen role/topology safely; it is not a configuration mode. |
46
+
47
+ ### Current versus target
48
+
49
+ Current implementation has contradictory mode and authority behavior:
50
+
51
+ - `src/lib/mode.ts`, `src/lib/cloud/mode.ts`, `src/lib/cloud/resolve.ts`, and `src/generated/storage-kit/mode.ts` disagree.
52
+ - `src/lib/store/index.ts` can let `getStore` silently choose SQLite for database-only configuration.
53
+ - `ApiStore` getters can convert remote failures into apparent absence.
54
+ - `src/daemon/index.ts` directly creates local authority.
55
+ - `src/sdk/index.ts` and `src/sdk/http.ts` both export `LoopsClient`, while handwritten remote transport and generated SDK behavior diverge.
56
+ - Business logic is duplicated or embedded across CLI, API, MCP, and runner. OpenAPI omits rename/history-prune request bodies and query parameters. MCP `run-now` schedules work while local execution can run inline.
57
+
58
+ Target implementation removes behavior selection from product modes and routes every surface through one application contract with explicit authority, persistence, and role configuration.
59
+
60
+ ### Source-evidence anchors
61
+
62
+ | Source | Evidence anchored here |
63
+ | --- | --- |
64
+ | `package.json` | Six declared binaries, 13 public export entries, and the public `./mode` compatibility surface. |
65
+ | `hasna.contract.json` | Product metadata that currently declares modes and mode-shaped runtime metadata. |
66
+ | `src/index.ts` | Root export surface and compatibility exposure. |
67
+ | `openapi/loops.json` | HTTP operation/generation contract, `Foundation.mode`, API status/version responses, and missing operation-shape coverage. |
68
+ | `src/api/index.ts` | Current HTTP adapter composition and application-boundary fragmentation. |
69
+ | `src/mcp/index.ts` | Current MCP tool composition, including `run-now` behavior. |
70
+ | `src/serve/index.ts` | Network server assembly, PostgreSQL runtime/auth/migrator separation, and signing/auth requirements. |
71
+ | `src/runner/index.ts` | Remote runner identity, claim, heartbeat, execution, and finalization composition. |
72
+ | `src/lib/scheduler.ts` | Current due selection, schedule advancement, retry, circuit, expiry, recovery, and execution policy. |
73
+ | `src/lib/store.ts` | Migration inventory evidence for total `daemonLeases` and expiration-filtered `activeDaemonLeases`. |
74
+ | `src/lib/storage/contract.ts` | Current policy-rich `LoopStorageContract`; evidence of migration debt at the application/storage boundary. |
75
+ | `src/lib/migration.ts` | Current workflows/loops/runs migration bundle and explicit skipped running/orphan run behavior. |
76
+ | `src/lib/storage/postgres-schema.ts` | PostgreSQL workflow-run provenance migration source. |
77
+ | `src/lib/storage/postgres-loop-storage.test.ts` | Targeted PostgreSQL provenance atomicity, conflict, and rollback test source. |
78
+ | `src/cli/index.test.ts` | Current mode, self-hosted/cloud command, migration, and compatibility expectations. |
79
+
80
+ This table is a compact traceability anchor, not proof that the target contract is already implemented. The binary/export counts and adapter-fragmentation findings remain subject to the evidence gates in this record.
81
+
82
+ ## 2. Orthogonal configuration axes
83
+
84
+ The following axes are independent. No single enum may collapse them.
85
+
86
+ | Axis | Choices | Rule |
87
+ | --- | --- | --- |
88
+ | Data authority | embedded application; remote HTTP application | Determines where a caller sends application commands. |
89
+ | Transport | in-process; CLI invocation; HTTP; MCP; SDK | Determines how a caller reaches the authority. |
90
+ | Persistence | SQLite; PostgreSQL | Chosen only inside an authority process. |
91
+ | Process role/capability | client; scheduler/daemon; runner/worker; server; admin | Enables explicit capabilities in a process. |
92
+ | Auth/tenant | embedded single-user context; authenticated multi-tenant context | Determines principal and tenant enforcement. |
93
+ | Topology | single machine; service deployment; AWS or other infrastructure | Describes placement, not behavior selection. |
94
+ | Readiness | unproven; tested; rehearsal-proven; production-ready | Is evidence status, not runtime configuration. |
95
+
96
+ Roles and topologies are not modes. Embedded SQLite authority may be packaged or hosted, but it is not the multi-tenant server role. The network multi-tenant server role requires explicitly configured PostgreSQL. An AWS deployment is still a topology, not a product mode, and a remote client never infers a storage engine from a DSN.
97
+
98
+ ## 3. Fail-closed authority and persistence precedence
99
+
100
+ The process role is resolved explicitly from the executable/subcommand, SDK constructor, or MCP launch contract before authority or persistence is resolved. Configuration then produces exactly one complete role plan or a typed configuration error.
101
+
102
+ ### Closed role-resolution matrix
103
+
104
+ | Explicit role | Authority and persistence | Complete required configuration | Absent configuration | Role-inapplicable or conflicting configuration |
105
+ | --- | --- | --- | --- | --- |
106
+ | Embedded client: CLI, SDK, or MCP | In-process `LoopsApplication`; SQLite | No authority inputs; optional SQLite location/configuration only | Defaults to SQLite | Remote endpoint/credential or any PostgreSQL DSN is rejected |
107
+ | Embedded scheduler/daemon | In-process `LoopsApplication`; SQLite | Explicit scheduler/daemon role; optional SQLite location/configuration only | Defaults to SQLite after the role is known | Remote client inputs or any PostgreSQL DSN is rejected |
108
+ | Remote client: CLI, SDK, or MCP | Remote HTTP `LoopsApplication` authority; no client-owned store | Complete endpoint plus client credential | Error; never creates SQLite | SQLite configuration, PostgreSQL DSNs, or incomplete remote inputs are rejected |
109
+ | Runner | Remote HTTP `LoopsApplication` authority; no runner-owned store | Complete endpoint, runner credential, and runner identity required by the claim/heartbeat protocol | Error; never creates SQLite | SQLite configuration, PostgreSQL DSNs, client-only credentials, or incomplete runner identity are rejected |
110
+ | Network multi-tenant server | HTTP authority; explicit PostgreSQL persistence | Complete runtime DSN, auth DSN, and signing/authentication inputs | Error; SQLite is not a server-role fallback | SQLite selection, remote-client authority inputs, privileged migrator DSN used as runtime DSN, or any partial server set is rejected |
111
+ | Admin/migrator | Explicit privileged administrative authority over PostgreSQL | Privileged DSN plus an explicit allowed administrative operation | Error; no default operation and no store creation | Runtime/client credentials used as privileged authority, missing operation, or mixed role configuration is rejected |
112
+
113
+ This matrix is closed: an unlisted role or role/configuration combination is unsupported and fails. Every partial, conflicting, ambiguous, or role-inapplicable configuration must fail validation **before any store, transport, scheduler, runner, or server is created**. Error details identify configuration categories without exposing credential values.
114
+
115
+ A PostgreSQL DSN never changes client authority. A remote transport, authentication, authorization, timeout, validation, or server failure is returned as a precise remote error and never falls back to SQLite or another local authority. The product makes no dual-authority, cache-and-spool, queued-write, or automatic reconciliation claim: one invocation has one selected authority.
116
+
117
+ Legacy mode inputs may be accepted only by a bounded compatibility parser that maps unambiguously into one row of this matrix and the orthogonal axes. Ambiguous, conflicting, partial, or role-inapplicable legacy input is rejected before resource creation.
118
+
119
+ ## 4. One `LoopsApplication` capability contract
120
+
121
+ `LoopsApplication` is the sole owner of domain policy. It owns domain commands, queries, validation, authorization decisions, state-transition rules, and the orchestration of storage transaction boundaries. It alone decides:
122
+
123
+ - due-selection rules and stable ordering;
124
+ - schedule advancement;
125
+ - overlap, circuit-breaker, retry, expiry, and recovery outcomes;
126
+ - workflow definition/run/step/event transition semantics;
127
+ - goal, plan-node, and goal-run transition semantics;
128
+ - whether `run-now` schedules, claims, or executes through an available runner capability.
129
+
130
+ Its capability contract includes, at minimum:
131
+
132
+ - create, read, list, update, rename, pause, resume, delete, and history-prune loop operations;
133
+ - schedule evaluation, due-work discovery, recover, expire, claim, attempt advancement, execution finalization, and overlap handling;
134
+ - run-now semantics that explicitly state whether the capability schedules work or executes it through an available runner;
135
+ - migration/provenance inspection and approved administrative operations;
136
+ - typed result and error contracts that preserve remote/server failures rather than translating them to absence.
137
+
138
+ Storage adapters expose only explicitly specified atomic persistence primitives. They may enforce database constraints, uniqueness, compare-and-swap preconditions, transaction isolation, tenant context, and RLS, but they do not choose lifecycle policy.
139
+
140
+ | Application-owned operation | Target storage primitive |
141
+ | --- | --- |
142
+ | Select due work and decide the next schedule | Read candidate state plus atomically compare-and-swap the application-supplied schedule/claim transition under an expected version or state |
143
+ | Apply overlap, circuit, retry, expiry, or recovery decision | Atomically persist the application-supplied transition and evidence when preconditions still match |
144
+ | Advance an attempt | Compare-and-swap the expected attempt/state so one advancement wins |
145
+ | Finalize a run | Compare-and-swap the claim token/expected running state to one terminal result so one finalizer wins |
146
+ | Transition workflow run/step/event state | Apply the application-supplied transition and append its event in one transaction under expected parent/version preconditions |
147
+ | Transition goal/plan-node/goal-run state | Apply the application-supplied transition and evidence in one transaction under expected state/version preconditions |
148
+ | Prune or migrate state | Apply an explicit application-approved entity set with integrity checks; storage does not decide what is disposable |
149
+
150
+ The existing policy-rich methods in `src/lib/storage/contract.ts` are migration debt, not the target boundary. CLI, API, MCP, daemon, scheduler, runner, server, and SDK code must not call policy-bearing legacy `Store`, `LoopStorageContract`, planner, or scheduler paths directly. During migration, such calls must be routed behind `LoopsApplication`, then reduced to the target atomic primitives.
151
+
152
+ Adapters are thin and own only boundary concerns:
153
+
154
+ | Surface | Adapter ownership |
155
+ | --- | --- |
156
+ | CLI | argument parsing, display, exit-code mapping, and compatibility aliases |
157
+ | HTTP API | request validation, authentication extraction, response mapping, and OpenAPI conformance |
158
+ | SDK | typed caller ergonomics over the API contract; no duplicate domain semantics |
159
+ | MCP | tool schemas and application capability invocation with behavior matching CLI/API |
160
+ | daemon/scheduler | process lifecycle and invocation of scheduling capabilities |
161
+ | runner | process lifecycle and invocation of execution/finalization capabilities |
162
+ | server/admin | authority assembly, persistence selection, tenant context, and administrative boundaries |
163
+
164
+ No adapter may reimplement lifecycle decisions, silently substitute a store, reinterpret a remote failure as missing data, or bypass `LoopsApplication` to invoke a policy-bearing legacy path.
165
+
166
+ ## 5. Executable, SDK, OpenAPI, and MCP direction
167
+
168
+ The canonical executable is `loops`, with explicit process subcommands for client, scheduler/daemon, runner, server, and admin responsibilities. It owns consistent configuration resolution and capability wiring.
169
+
170
+ The existing six binaries remain initially for compatibility. Five auxiliary binaries become thin shims that delegate to the canonical executable, preserve established invocation compatibility where possible, emit deprecation guidance, and contain no independent authority or business logic. Shim compatibility must be tested from packed artifacts, not only source-tree execution.
171
+
172
+ There is one primary public SDK. The generated remote client is derived from authoritative OpenAPI and is the remote transport foundation; handwritten transport must either be removed or reduced to a narrowly scoped generated-client wrapper with no divergent endpoint semantics. `src/sdk/index.ts` and `src/sdk/http.ts` must not continue to expose competing `LoopsClient` contracts.
173
+
174
+ OpenAPI is authoritative for every HTTP operation, including rename and history-prune request bodies and all supported query parameters. Generation drift is a release-blocking failure. MCP tools must map to the same application capabilities and return equivalent semantic outcomes. In particular, `run-now` must have one documented contract across MCP, CLI, HTTP, SDK, and embedded execution: scheduling versus inline execution must be an explicit capability/role distinction, never an adapter accident.
175
+
176
+ ## 6. Storage and tenancy invariants
177
+
178
+ Both SQLite and PostgreSQL implementations must prove the same externally visible lifecycle invariants:
179
+
180
+ - recover, expire, and claim transitions are atomic;
181
+ - `expiresAt` is persisted and enforced;
182
+ - `overlap=skip` is decided by `LoopsApplication` and atomically enforced by an application-supplied storage transition;
183
+ - an attempt advances exactly once;
184
+ - concurrent finalization has one winner and deterministic loser behavior;
185
+ - recovery and attempt history preserve workflow provenance;
186
+ - typed remote errors remain distinguishable from absence, validation errors, and authorization errors.
187
+
188
+ For multi-tenant PostgreSQL authority:
189
+
190
+ - tenant context is transaction-local;
191
+ - row-level security is forced;
192
+ - application, migration/admin, and operational roles are separated;
193
+ - authorization is enforced in both application and database boundaries where applicable.
194
+
195
+ Migrations are ordered, immutable, and checksummed. Unknown, altered, missing, or out-of-order migrations fail closed. SQLite backups use `VACUUM INTO` and require restore rehearsal. PostgreSQL transfer requires source/destination integrity proof, including expected migration identity, counts, hashes/checksums, provenance, and orphan detection before cutover is eligible.
196
+
197
+ ### PostgreSQL workflow-provenance evidence
198
+
199
+ Current source includes a PostgreSQL workflow-run provenance migration and a targeted PostgreSQL test for atomic initial provenance, idempotency conflict, and rollback behavior. That is implementation and targeted test-source evidence; it is not an absence of implementation, but neither is it integrated parity proof.
200
+
201
+ | Provenance claim | State |
202
+ | --- | --- |
203
+ | PostgreSQL provenance migration exists in source | PRESENT |
204
+ | Targeted atomic conflict/rollback test exists in source | PRESENT |
205
+ | Targeted test executed successfully for the final candidate | UNKNOWN |
206
+ | Shared SQLite/PostgreSQL parity suite | RED |
207
+ | Cross-backend workflow/goal provenance semantics | RED |
208
+ | Object/filesystem provenance semantics | UNKNOWN |
209
+ | Same-final-SHA integrated evidence | RED |
210
+
211
+ ## 7. Current P0 gaps: RED
212
+
213
+ The following gaps are **RED**. They block any embedded/server parity, safe-cutover, or readiness claim until closed with the evidence gates below.
214
+
215
+ | P0 gap | State | Claim blocked |
216
+ | --- | --- | --- |
217
+ | Hosted recovery and attempts parity | RED | hosted/server lifecycle parity |
218
+ | `expiresAt` parity | RED | expiration correctness parity |
219
+ | `overlap=skip` parity | RED | overlap safety parity |
220
+ | PostgreSQL workflow provenance integrated parity | RED | cross-backend audit/recovery provenance parity; implementation and targeted test source already exist |
221
+ | Complete no-loss SQLite-to-PostgreSQL cutover | RED | lossless migration/cutover |
222
+ | Broad remote error masking | RED | reliable remote absence/error semantics |
223
+
224
+ The mode disagreement, silent database-only SQLite selection, direct daemon local authority creation, duplicated client exports, OpenAPI omissions, and adapter-level behavior divergence are also blocking architecture defects. They must be removed or contained behind tested compatibility boundaries before the target contract can be declared implemented.
225
+
226
+ ## 8. Embedded single-user and server multi-tenant authentication
227
+
228
+ Embedded SQLite authority is a single-user deployment contract. It may use local process identity and does not imply multi-tenant hosting, remote credential validation, or tenant isolation.
229
+
230
+ Network server authority is a multi-tenant contract. It requires explicit selection of the server role, PostgreSQL runtime/auth configuration, signing/authentication inputs, authenticated principals, explicit tenant resolution, transaction-local tenant context, FORCE RLS, role separation, and precise authorization failures. Role is explicit before defaults are considered: absent authority configuration defaults to SQLite only for the embedded-capable roles listed in the closed matrix, never for runner, network server, or admin/migrator.
231
+
232
+ ## 9. Compatibility, migration, refusal gates, rollback, and deprecation
233
+
234
+ Compatibility is boundary-only and time-bounded:
235
+
236
+ - legacy mode values follow the exact mapping/refusal table below and otherwise fail closed;
237
+ - all six binaries follow the exhaustive binary disposition table below; the five non-canonical binaries begin as thin shims;
238
+ - all 13 package exports follow the exhaustive export disposition table below; none is removed or internalized before its consumer-evidence gate;
239
+ - compatibility shims must not preserve divergent stores, schedulers, transports, or lifecycle semantics.
240
+
241
+ ### Legacy surface disposition
242
+
243
+ Canonical status and version responses must emit orthogonal `authority`, `transport`, `persistence`, `processRole`, `authTenant`, `topology`, and `readiness` fields rather than a product mode. A legacy mode-shaped output may remain only as a bounded, explicitly deprecated compatibility field when removal would break a verified consumer; it must be derived from the canonical fields and must not control behavior.
244
+
245
+ #### Exact legacy value mapping and refusal
246
+
247
+ | Legacy value | Accepted boundary and complete requirements | Target mapping | Mandatory refusal |
248
+ | --- | --- | --- | --- |
249
+ | `local` | Legacy embedded-capable CLI, SDK, MCP, or daemon boundary only, with no remote or PostgreSQL authority inputs | Explicit embedded client or embedded scheduler/daemon role plus SQLite | Reject with endpoint/credential, any PostgreSQL DSN, runner/server/admin role, or any conflicting explicit role |
250
+ | `self_hosted` | Value alone is ambiguous and rejected. At a legacy remote-client boundary, accept only with complete endpoint plus credential. At an explicit network-server boundary, accept only with complete runtime/auth DSNs and signing/authentication inputs | Complete legacy remote-client input maps to remote client; complete explicit server input maps to network multi-tenant server | Reject incomplete input, every other boundary, and every conflict; never infer client versus server from the value |
251
+ | `cloud` | Value alone is rejected. At a legacy remote-client boundary, accept only with complete endpoint plus credential | Remote client only; the value never selects a provider, topology, or persistence | Reject at embedded, network-server, admin/migrator, and runner boundaries, and on every conflict; a distinct complete explicit role plan does not authorize this value to alter that plan |
252
+
253
+ Canonical outputs never emit `local`, `self_hosted`, or `cloud` as behavioral state. A verified consumer may temporarily receive a derived, explicitly deprecated compatibility field; that field is output-only, cannot feed authority resolution, and is removed only after consumer evidence and its published deprecation gate.
254
+
255
+ | Current surface | Compatibility input/output behavior | Target mapping | Owner/test | Removal gate |
256
+ | --- | --- | --- | --- | --- |
257
+ | `src/lib/mode.ts` enum/resolver | Accepts and emits mode-shaped values | Compatibility input follows the exact value table above; canonical resolver emits orthogonal fields only | Role resolver; closed matrix, value/refusal, partial/conflict, and output tests | No internal policy callers; verified consumers migrated; published deprecation and semver gates complete |
258
+ | `hasna.contract.json` modes | Declares product modes and mode-shaped runtime metadata | Declare capabilities, authority/persistence support, roles, auth/tenant contract, topology compatibility, and readiness separately | Contract-schema validation | Downstream contract readers support orthogonal fields; legacy schema window complete |
259
+ | `openapi/loops.json` `Foundation.mode` and API status/version responses | Emits mode-shaped service identity | Canonical response uses orthogonal fields; deprecated `mode` output only if a verified consumer requires it | OpenAPI validation, generation-drift check, generated SDK response tests | Generated clients and verified consumers no longer read `mode` |
260
+ | CLI `mode`, `self-hosted`, and `cloud` commands plus `src/cli/index.test.ts` | Accepts legacy commands/config and emits mode-shaped status | Compatibility aliases dispatch only through the exact value table and canonical role subcommands; status is orthogonal | CLI value/refusal, resolver, status-output, and packed binary tests | Replacement commands documented; verified consumers migrated; published deprecation gate complete |
261
+ | SDK constructors/options | Embedded and remote clients can be selected through overlapping constructors/options | Explicit embedded and remote constructors/options feed one role resolver and one public SDK | SDK unit, generated-client, and resolver matrix tests | Ambiguous constructors removed only after typed migration path, verified consumer evidence, and deprecation gate |
262
+ | Documentation and configuration environment inputs | Documents or accepts mode-shaped environment values | Document explicit role, authority, persistence, auth/tenant, and topology inputs; legacy env input follows the exact value table | Documentation examples plus configuration value/refusal matrix tests | Config migration guide shipped; verified consumers migrated; deprecated parser removal gate complete |
263
+
264
+ The six binary entries and all 13 package export entries are exhaustively dispositioned below; their inventory is not deferred.
265
+
266
+ #### Exhaustive binary disposition
267
+
268
+ | Binary | Target and shim behavior | Required parity test | Removal gate |
269
+ | --- | --- | --- | --- |
270
+ | `loops` | Canonical executable and subcommand dispatcher; owns role resolution and adapter wiring | Packed invocation, help/version, role resolution, argument, exit-code, and error-contract tests | Canonical binary is retained; any future replacement requires verified consumer evidence and an explicit superseding contract |
271
+ | `loops-daemon` | Thin forwarder to `loops daemon`; preserves supported arguments, signals, stdout/stderr, and exit status without owning scheduler policy | Packed shim versus `loops daemon` argument/config-error/signal/exit parity | Verified consumer inventory and migration, replacement available, deprecation window and semver gate complete |
272
+ | `loops-api` | Thin forwarder to the `loops api/status` adapter; contains no independent HTTP or status policy | Packed shim versus canonical API/status startup, probe/status response, argument, error, and exit parity | Verified consumer inventory and migration, endpoint/command replacement documented, deprecation window and semver gate complete |
273
+ | `loops-serve` | Thin forwarder to `loops server/admin`; contains no independent authority, migration, or auth policy | Packed shim versus canonical server/admin argument routing, startup refusal, signal, error, and exit parity | Verified server/admin consumers migrated, operational replacement rehearsed, deprecation window and semver gate complete |
274
+ | `loops-runner` | Thin forwarder to `loops runner`; contains no claim, heartbeat, retry, or finalization policy | Packed shim versus canonical runner identity/config refusal, signal, error, and exit parity | Verified runner consumers migrated, replacement operationally proven, deprecation window and semver gate complete |
275
+ | `loops-mcp` | Thin forwarder to `loops mcp`; preserves MCP transport framing while owning no tool semantics | Packed shim versus canonical MCP handshake, tool schema, framing, semantic error, and exit parity | Verified MCP consumers migrated, replacement compatibility proven, deprecation window and semver gate complete |
276
+
277
+ #### Exhaustive package export disposition
278
+
279
+ Raw storage and policy exports are compatibility debt and must not become the new application contract. Every deprecate/internalize target below remains import-compatible until verified consumer evidence, a replacement path, and the stated removal gate exist.
280
+
281
+ | Package export | Target disposition | Compatibility behavior | Owner/test | Removal or internalization gate |
282
+ | --- | --- | --- | --- | --- |
283
+ | `.` | **Keep** as canonical root, narrowed to application/domain types and approved top-level capabilities | Existing root names may forward through deprecated aliases, but no alias may bypass `LoopsApplication` | Public API owner; packed root import, type/API-surface, and no-policy-bypass tests | No removal planned; any breaking narrowing requires per-symbol consumer inventory, replacements, deprecation, and semver evidence |
284
+ | `./sdk` | **Keep** as the one primary public SDK | Preserve supported constructors through explicit embedded/remote adapters and the closed resolver | SDK owner; constructor/refusal, generated-client, type, and packed import tests | No removal planned; constructor removal requires verified consumer migration and semver gate |
285
+ | `./sdk/http` | **Deprecate**, then fold into `./sdk` | Forward to the generated remote transport exposed by the primary SDK; no competing `LoopsClient` semantics | SDK transport owner; generated parity, response/error, type, and packed import tests | Verified consumers migrated to `./sdk`, replacement stable, deprecation window and semver gate complete |
286
+ | `./serve` | **Deprecate**, then internalize behind `loops server/admin` | Forward only to canonical server/admin assembly; expose no independent authority policy | Server/admin adapter owner; startup-refusal, RLS/role, argument, and packed import tests | Verified programmatic consumers migrated, operational replacement proven, deprecation window and semver gate complete |
287
+ | `./mcp` | **Keep** as a thin public MCP adapter | Preserve MCP integration while routing every capability through `LoopsApplication` | MCP adapter owner; tool-schema, behavior parity, error, and packed import tests | No removal planned; any future removal requires verified consumer evidence and a replacement contract |
288
+ | `./api` | **Keep** as a thin public HTTP adapter | Preserve supported server integration points while deriving behavior and schema from `LoopsApplication` and OpenAPI | HTTP adapter owner; OpenAPI integration, auth/error, generation-drift, and packed import tests | No removal planned; any future removal requires verified consumer evidence and a replacement contract |
289
+ | `./runner` | **Deprecate**, then internalize behind `loops runner` | Forward programmatic entry points to canonical runner assembly; expose no claim/finalization policy | Runner adapter owner; identity/config refusal, claim/heartbeat/finalize delegation, and packed import tests | Verified programmatic consumers migrated, process replacement proven, deprecation window and semver gate complete |
290
+ | `./mode` | **Deprecate**, then remove | Preserve only the exact legacy value mapping/refusal behavior; canonical output is orthogonal | Role resolver owner; value/refusal, conflict, output, type, and packed import tests | Verified consumers migrated, no internal behavioral callers, deprecation window and semver gate complete |
291
+ | `./storage` | **Deprecate**, then internalize | Compatibility facade may forward storage access needed by existing consumers, but it cannot define or expose new lifecycle policy | Application/storage-boundary owner; compatibility, no-policy-bypass, and packed import tests | Per-symbol consumer inventory complete, application replacements shipped, consumers migrated, deprecation and semver gates complete |
292
+ | `./storage/contract` | **Deprecate**, then internalize the policy-rich contract | Existing types/methods remain compatibility-only while callers migrate to `LoopsApplication` and atomic persistence primitives | Application/storage-boundary owner; type compatibility, direct-call prohibition, and primitive contract tests | No external or internal policy-bearing callers, replacements proven on both backends, consumer/deprecation/semver gates complete |
293
+ | `./storage/sqlite` | **Deprecate direct application use**, then internalize behind embedded authority | Preserve existing imports without adding policy; implementation serves only the atomic persistence boundary | SQLite persistence owner; shared backend suite, migration/restore, no-policy-bypass, and packed import tests | Verified direct consumers migrated, embedded application replacement complete, shared parity and deprecation/semver gates complete |
294
+ | `./storage/postgres` | **Deprecate direct application use**, then internalize behind server/admin authority | Preserve existing imports without adding policy; implementation serves only atomic persistence, constraints, and RLS | PostgreSQL persistence owner; shared backend, disposable PostgreSQL, RLS/role, and packed import tests | Verified direct consumers migrated, server/admin replacement complete, shared parity and deprecation/semver gates complete |
295
+ | `./storage/postgres-schema` | **Deprecate direct use**, then internalize behind admin/migrator | Preserve migration/schema inspection needed by existing consumers without granting runtime policy ownership | Admin/migrator owner; ordered migration hash, unknown-migration refusal, role separation, and packed import tests | Verified consumers migrated to explicit admin operations, migration replacement proven, deprecation and semver gates complete |
296
+
297
+ ### Authoritative-state migration classification
298
+
299
+ SQLite-to-PostgreSQL migration is a deliberate admin/migrator operation, not implicit client behavior. Every state category must be assigned exactly one candidate disposition: **transferred**, **deterministically rebuilt**, **intentionally volatile**, or **blocking**. Only a daemon lease proven expired and non-authoritative may be intentionally volatile; no other category currently has an approved deterministically rebuilt or intentionally volatile disposition.
300
+
301
+ | Authoritative-state category | Candidate classification | Current evidence and required disposition |
302
+ | --- | --- | --- |
303
+ | Workflows/definitions | Transferred | Current bundle includes workflows; per-entity counts, hashes, provenance, and destination proof are still required |
304
+ | Loops | Transferred | Current bundle includes loops; schedule/config hashes, parent references, and destination proof are still required |
305
+ | Runs/attempts | Blocking | Current bundle includes run rows but may skip running or orphan rows; candidate requires quiescence and zero skipped authoritative rows |
306
+ | Expired daemon leases | Intentionally volatile, conditionally | Each lease must be proven expired at the quiescence boundary and non-authoritative; disposal requires an explicit record containing lease identity, observed expiry, and disposition evidence |
307
+ | Active daemon leases | Blocking | Every lease counted by `activeDaemonLeases` is an active-authority signal and blocks migration until it expires or is cleanly relinquished and re-observed as inactive |
308
+ | Receipts/evidence | Blocking | Not covered by the current general bundle; transfer or an approved deterministic rebuild specification is required |
309
+ | Invocations | Blocking | Not covered by the current general bundle; identity, ordering, and provenance must be preserved |
310
+ | Work items | Blocking | Not covered by the current general bundle; ownership, admission, and references must be preserved |
311
+ | Workflow runs/steps/events | Blocking | Not covered by the current general bundle; transition/event order and parent integrity must be preserved |
312
+ | Goals/plan nodes/goal runs | Blocking | Not covered by the current general bundle; graph, lineage, status, usage, and evidence integrity must be preserved |
313
+ | Tenant/auth/config state | Blocking | Requires an explicit security-preserving transfer or approved rebuild/bootstrap procedure with role and tenant proof |
314
+ | Filesystem/object provenance | Blocking | Requires an inventory and content/object hashes, references, ownership, and restore semantics; database rows alone are insufficient |
315
+
316
+ The current bundle handles workflows, loops, and runs only, and it explicitly permits counted skips for running and orphan run rows. Therefore the general no-loss claim remains **RED**. Migration eligibility requires a quiesced source, zero active daemon leases, an explicit disposal record for every expired daemon lease, zero skipped authoritative rows, per-entity source/destination counts and hashes, provenance checks, orphan/reference reports, migration ledger identity, and successful restore proof. Any active lease, unrecorded expired-lease disposal, nonzero authoritative skip, unmatched entity, unresolved orphan, missing category disposition, or unproved filesystem/object reference is a refusal.
317
+
318
+ Rollback is defined before deprecation: retain a verified source backup, preserve ordered migration state, document the exact reversal boundary, and refuse destructive cutover steps when restoration proof is absent. Deprecate legacy modes, binaries, exports, and handwritten transport only after packed-shim, external-consumer, and migration evidence supports removal. External consumers remain **UNKNOWN** until independently inventoried.
319
+
320
+ ## 10. Evidence gates and implementation sequence
321
+
322
+ Implementation must advance in this order; later claims are blocked by earlier RED or UNKNOWN evidence.
323
+
324
+ 1. Define and test the closed authority/persistence/role resolver, including every supported row and all absent, partial, conflicting, ambiguous, and role-inapplicable configurations before resource creation.
325
+ 2. Build the shared `LoopsApplication` capability contract and route embedded SQLite paths through it.
326
+ 3. Add SQLite lifecycle tests for atomic recover/expire/claim, `expiresAt`, `overlap=skip`, exactly-once attempt advance, and one-winner finalize.
327
+ 4. Run the same shared suite against SQLite and disposable PostgreSQL, with tenant/RLS/role tests for PostgreSQL.
328
+ 5. Make OpenAPI complete and authoritative; fail CI on generation drift and test generated-client behavior.
329
+ 6. Route CLI, HTTP, MCP, daemon, runner, and server/admin through thin adapters; prove `run-now` parity and precise remote errors.
330
+ 7. Convert auxiliary binaries and compatibility exports to packed, tested shims.
331
+ 8. Implement the complete authoritative-state classification and transfer/refusal checks; require quiescence, zero active daemon leases, an explicit disposal record for every expired daemon lease, zero skipped authoritative rows, and per-entity counts, hashes/checksums, provenance, and orphan/reference reports.
332
+ 9. Perform SQLite backup/restore rehearsal and PostgreSQL transfer/restore proof in a disposable environment.
333
+ 10. Run every mandatory resolver, SQLite/PostgreSQL parity, tenant/RLS/role, OpenAPI generation, generated-client, MCP, packed-shim, migration-integrity, and restore gate on the **same final clean integrated candidate SHA**.
334
+ 11. Record that exact SHA with the complete evidence set and separately verify any live AWS state or external consumer inventory before making those claims.
335
+
336
+ Evidence from different SHAs cannot be composed into an integrated pass. Any source, schema, migration, or OpenAPI specification change invalidates the integrated evidence and requires the complete mandatory gate set to run again on the new final clean integrated candidate SHA.
337
+
338
+ No production cutover, parity announcement, or readiness statement is permitted merely because a code path or targeted test exists.
339
+
340
+ ## 11. Objective checklist
341
+
342
+ | Objective | State | Required proof |
343
+ | --- | --- | --- |
344
+ | One application contract owns lifecycle semantics | UNKNOWN | adapter routing and capability tests at an exact SHA |
345
+ | No target public product-mode enum | RED | legacy inputs isolated; target configuration uses orthogonal axes |
346
+ | Absent authority configuration defaults to SQLite only for explicit embedded-capable roles | UNKNOWN | closed resolver matrix tests before resource creation |
347
+ | Remote clients require complete endpoint plus credential | UNKNOWN | closed resolver and remote integration tests |
348
+ | Runner requires complete remote authority and runner identity | UNKNOWN | claim/heartbeat identity and role-rejection tests |
349
+ | Network multi-tenant server requires PostgreSQL runtime/auth DSNs and signing/auth inputs | RED | closed resolver, server startup refusal, RLS, and role-separation tests |
350
+ | Admin/migrator requires explicit privileged DSN and operation | RED | operation allowlist, privilege separation, and refusal tests |
351
+ | Remote failures never fall back or become absence | RED | typed-error tests across API store, SDK, CLI, and MCP |
352
+ | SQLite/PostgreSQL lifecycle parity | RED | shared suite passing against SQLite and disposable PostgreSQL |
353
+ | Hosted recovery/attempts, `expiresAt`, and `overlap=skip` parity | RED | P0 regression suite and transaction evidence |
354
+ | PostgreSQL workflow provenance integrated parity | RED | existing migration and targeted test plus executed shared cross-backend suite at the final SHA |
355
+ | Complete authoritative-state SQLite-to-PostgreSQL no-loss cutover | RED | quiescence, zero active daemon leases, recorded expired-lease disposal, zero authoritative skips, category disposition, per-entity counts/hashes/provenance/orphans, and restore proof |
356
+ | OpenAPI and generated SDK are authoritative | RED | complete spec, generation-drift check, generated-client tests |
357
+ | MCP behavior parity | RED | equivalent capability and `run-now` tests |
358
+ | Five auxiliary binaries are thin packed shims | UNKNOWN | packed-artifact invocation tests |
359
+ | 13 compatibility exports have an owned deprecation path | UNKNOWN | export inventory and compatibility tests |
360
+ | Embedded single-user versus server multi-tenant auth boundary | UNKNOWN | authentication, transaction-local tenant, FORCE RLS, and role tests |
361
+ | All mandatory gates pass on one final clean integrated SHA | RED | one recorded SHA; any source/schema/migration/spec change reruns all mandatory gates |
362
+ | Live AWS readiness | UNKNOWN | independent live-environment evidence |
363
+ | External-consumer impact | UNKNOWN | independent consumer inventory |
364
+
365
+ Until every relevant RED is closed and every required UNKNOWN is evidenced, Loops must not claim parity, live AWS readiness, lossless migration, or absence of external consumers.