@hasna/todos 0.15.33 → 0.15.35

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 (43) hide show
  1. package/dist/cli/cloud-router.d.ts +33 -47
  2. package/dist/cli/cloud-router.d.ts.map +1 -1
  3. package/dist/cli/commands/project-commands.d.ts.map +1 -1
  4. package/dist/cli/commands/task-commands.d.ts.map +1 -1
  5. package/dist/cli/index.js +344 -127
  6. package/dist/cli/stage-a.d.ts +3 -1
  7. package/dist/cli/stage-a.d.ts.map +1 -1
  8. package/dist/contracts.js +115 -27
  9. package/dist/db/migrations.d.ts.map +1 -1
  10. package/dist/db/projects.d.ts +23 -0
  11. package/dist/db/projects.d.ts.map +1 -1
  12. package/dist/db/schema.d.ts.map +1 -1
  13. package/dist/index.js +275 -116
  14. package/dist/json-contracts.d.ts.map +1 -1
  15. package/dist/lib/assignee-validation.d.ts.map +1 -1
  16. package/dist/lib/import-export-bridge.d.ts.map +1 -1
  17. package/dist/lib/onboarding-fixtures.d.ts.map +1 -1
  18. package/dist/mcp/index.js +285 -113
  19. package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
  20. package/dist/mcp.js +3 -2
  21. package/dist/project-registration/authority.d.ts.map +1 -1
  22. package/dist/project-registration/sqlite.d.ts.map +1 -1
  23. package/dist/project-registration.js +185 -40
  24. package/dist/registry.js +115 -27
  25. package/dist/release-provenance.json +5 -5
  26. package/dist/sdk/v1.generated.d.ts +3 -0
  27. package/dist/sdk/v1.generated.d.ts.map +1 -1
  28. package/dist/server/index.js +1268 -7051
  29. package/dist/server/openapi.d.ts +13 -0
  30. package/dist/server/openapi.d.ts.map +1 -1
  31. package/dist/server/v1.d.ts.map +1 -1
  32. package/dist/storage/config.d.ts +24 -4
  33. package/dist/storage/config.d.ts.map +1 -1
  34. package/dist/storage/hybrid.d.ts +5 -3
  35. package/dist/storage/hybrid.d.ts.map +1 -1
  36. package/dist/storage.js +158 -34
  37. package/dist/task-manifest.js +3 -3
  38. package/dist/testing.d.ts +16 -7
  39. package/dist/testing.d.ts.map +1 -1
  40. package/dist/testing.js +10 -6
  41. package/dist/types/index.d.ts +5 -2
  42. package/dist/types/index.d.ts.map +1 -1
  43. package/package.json +3 -2
@@ -114,6 +114,10 @@ export declare function buildV1OpenApiDocument(version?: string): {
114
114
  readonly task_counter: {
115
115
  readonly type: "number";
116
116
  };
117
+ readonly parent_id: {
118
+ readonly type: "string";
119
+ readonly nullable: true;
120
+ };
117
121
  readonly created_at: {
118
122
  readonly type: "string";
119
123
  };
@@ -1972,6 +1976,10 @@ export declare function buildV1OpenApiDocument(version?: string): {
1972
1976
  type: string;
1973
1977
  minLength: number;
1974
1978
  };
1979
+ parent_id: {
1980
+ type: string;
1981
+ minLength: number;
1982
+ };
1975
1983
  };
1976
1984
  };
1977
1985
  UpdateProjectInput: {
@@ -1991,6 +1999,11 @@ export declare function buildV1OpenApiDocument(version?: string): {
1991
1999
  type: string;
1992
2000
  nullable: boolean;
1993
2001
  };
2002
+ parent_id: {
2003
+ type: string;
2004
+ minLength: number;
2005
+ nullable: boolean;
2006
+ };
1994
2007
  };
1995
2008
  };
1996
2009
  RenameProjectInput: {
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/server/openapi.ts"],"names":[],"mappings":"AAi3BA,wBAAgB,sBAAsB,CAAC,OAAO,SAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6jEnE"}
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/server/openapi.ts"],"names":[],"mappings":"AAk3BA,wBAAgB,sBAAsB,CAAC,OAAO,SAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+jEnE"}
@@ -1 +1 @@
1
- {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../src/server/v1.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAuB,oBAAoB,EAAmD,MAAM,0BAA0B,CAAC;AAC3I,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,oCAAoC,EACpC,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EACjB,MAAM,YAAY,CAAC;AA0BpB,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,OAAO,gBAAgB,CAAC;IACtC,YAAY,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACxC,iBAAiB,CAAC,EAAE,OAAO,sBAAsB,CAAC;IAClD,gBAAgB,CAAC,EAAE,OAAO,qBAAqB,CAAC;IAChD,+BAA+B,CAAC,EAAE,OAAO,oCAAoC,CAAC;IAC9E,wBAAwB,CAAC,EAAE,OAAO,6BAA6B,CAAC;CACjE;AAmYD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,CAiB1E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAapE;AA4ED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,YAAY,GAAE,qBAA0B,GACvC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAkuC1B"}
1
+ {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../src/server/v1.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAuB,oBAAoB,EAAmD,MAAM,0BAA0B,CAAC;AAC3I,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,oCAAoC,EACpC,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EACjB,MAAM,YAAY,CAAC;AA0BpB,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,OAAO,gBAAgB,CAAC;IACtC,YAAY,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACxC,iBAAiB,CAAC,EAAE,OAAO,sBAAsB,CAAC;IAClD,gBAAgB,CAAC,EAAE,OAAO,qBAAqB,CAAC;IAChD,+BAA+B,CAAC,EAAE,OAAO,oCAAoC,CAAC;IAC9E,wBAAwB,CAAC,EAAE,OAAO,6BAA6B,CAAC;CACjE;AAuYD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,CAiB1E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAapE;AA4ED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,YAAY,GAAE,qBAA0B,GACvC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAkuC1B"}
@@ -2,8 +2,9 @@
2
2
  * The single data-backend switch (owner directive 2026-07-29, knowledge
3
3
  * k_ms3e6v41_zbe7m8): storage is either the local SQLite file or PostgreSQL.
4
4
  * The former deployment-mode axis (local / remote / self_hosted / cloud /
5
- * hybrid) is gone — legacy env tokens are normalized onto these two arms at the
6
- * parse boundary and nowhere else.
5
+ * hybrid) is gone — and since 2026-08-15 the storage-mode env variables are
6
+ * banned outright: their mere presence is a hard error. The backend is derived
7
+ * from HASNA_TODOS_DATABASE_URL: set selects postgresql, unset selects sqlite.
7
8
  */
8
9
  export type TodosStorageBackend = "sqlite" | "postgres";
9
10
  /** @deprecated Renamed to {@link TodosStorageBackend}; values collapsed to sqlite|postgres. */
@@ -39,7 +40,6 @@ export interface TodosStorageConfig {
39
40
  sync: TodosSyncConfig;
40
41
  }
41
42
  export declare const TODOS_STORAGE_ENV: {
42
- readonly mode: "HASNA_TODOS_STORAGE_MODE";
43
43
  readonly shadow: "HASNA_TODOS_SHADOW";
44
44
  readonly databaseUrl: "HASNA_TODOS_DATABASE_URL";
45
45
  readonly databaseSsl: "HASNA_TODOS_DATABASE_SSL";
@@ -56,7 +56,6 @@ export declare const TODOS_STORAGE_ENV: {
56
56
  readonly syncDryRun: "HASNA_TODOS_SYNC_DRY_RUN";
57
57
  };
58
58
  export declare const TODOS_STORAGE_FALLBACK_ENV: {
59
- readonly mode: "TODOS_STORAGE_MODE";
60
59
  readonly shadow: "TODOS_SHADOW";
61
60
  readonly databaseUrl: "TODOS_DATABASE_URL";
62
61
  readonly databaseSsl: "TODOS_DATABASE_SSL";
@@ -72,6 +71,13 @@ export declare const TODOS_STORAGE_FALLBACK_ENV: {
72
71
  readonly syncBatchSize: "TODOS_SYNC_BATCH_SIZE";
73
72
  readonly syncDryRun: "TODOS_SYNC_DRY_RUN";
74
73
  };
74
+ /**
75
+ * The retired storage-mode variables (owner directive 2026-08-15). Their mere
76
+ * presence — even blank — is a hard error on the server path, exactly as on the
77
+ * client: the backend is derived from HASNA_TODOS_DATABASE_URL, never selected
78
+ * by a mode variable.
79
+ */
80
+ export declare const REMOVED_STORAGE_MODE_ENV_KEYS: readonly ["HASNA_TODOS_STORAGE_MODE", "HASNA_TODOS_MODE", "TODOS_STORAGE_MODE", "TODOS_MODE"];
75
81
  export declare const CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER";
76
82
  export declare const CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH";
77
83
  export declare const CANONICAL_TODOS_RDS_DATABASE = "todos";
@@ -90,9 +96,23 @@ export declare function isTodosPostgresBackend(config: TodosStorageConfig): bool
90
96
  /** @deprecated Renamed to {@link isTodosPostgresBackend}. */
91
97
  export declare function isTodosRemoteStorageEnabled(config: TodosStorageConfig): boolean;
92
98
  export declare function assertTodosRemoteStorageConfig(config: TodosStorageConfig): void;
99
+ /**
100
+ * Parse a data-backend token. The deployment-mode vocabulary is gone and its
101
+ * variables are banned (owner directive 2026-08-15), so only the two canonical
102
+ * backend tokens — plus the `postgresql` engine-name spelling — are accepted;
103
+ * every deployment-mode token is rejected as removed.
104
+ */
93
105
  export declare function parseStorageBackend(value: string | undefined): TodosStorageBackend;
94
106
  /** @deprecated Renamed to {@link parseStorageBackend}; values collapsed to sqlite|postgres. */
95
107
  export declare function parseStorageMode(value: string | undefined): TodosStorageBackend;
108
+ /**
109
+ * Resolve the server data backend from the environment. The retired
110
+ * storage-mode variables are a hard error when present; otherwise the backend
111
+ * is derived from HASNA_TODOS_DATABASE_URL — set selects postgresql, unset
112
+ * selects the on-box SQLite file. One carve-out: a shadow mirror
113
+ * (HASNA_TODOS_SHADOW=1) keeps the SQLite file as the source of truth and
114
+ * treats the DSN as the mirror target, so the backend stays sqlite there.
115
+ */
96
116
  export declare function getTodosStorageBackend(env?: TodosStorageEnv): TodosStorageBackend;
97
117
  /** @deprecated Renamed to {@link getTodosStorageBackend}. */
98
118
  export declare function getTodosStorageMode(env?: TodosStorageEnv): TodosStorageBackend;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/storage/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/storage/config.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Explicit migration/sync machinery: a local-SQLite adapter that can push/pull
3
3
  * snapshots against the Postgres sync tables. This is NOT an arm of the
4
- * data-backend switch — no `HASNA_TODOS_STORAGE_MODE` value selects it; callers
5
- * construct it directly when they need a cross-store sync (e.g. cutover
6
- * tooling). The switch itself has exactly two arms: sqlite | postgres.
4
+ * data-backend switch — no environment value selects it; callers construct it
5
+ * directly when they need a cross-store sync (e.g. cutover tooling). The
6
+ * switch itself has exactly two arms, sqlite | postgres, derived from
7
+ * HASNA_TODOS_DATABASE_URL since the storage-mode variables were removed
8
+ * (owner directive 2026-08-15).
7
9
  */
8
10
  import { type PostgresTodosSyncPushResult, type PostgresTodosSyncStore, type PullPostgresTodosSnapshotOptions, type TodosPostgresQueryClient } from "./postgres-sync.js";
9
11
  import { type CreateLocalSqliteTodosStorageAdapterOptions } from "./local-sqlite.js";
@@ -1 +1 @@
1
- {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/storage/hybrid.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,KAAK,gCAAgC,EACrC,KAAK,wBAAwB,EAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,2CAA2C,EACjD,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EAEzB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,2BAA2B,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAClF,YAAY,CAAC,OAAO,CAAC,EAAE,gCAAgC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC3H,QAAQ,CAAC,OAAO,CAAC,EAAE,gCAAgC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAC5H;AAED,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACpE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;CACxC;AAED,MAAM,WAAW,sCAAsC;IACrD,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,KAAK,CAAC,EAAE,2CAA2C,CAAC;IACpD,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,cAAc,CAAC,EAAE,wBAAwB,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sCAAsC,GAC9C,yBAAyB,CAqD3B"}
1
+ {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/storage/hybrid.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,KAAK,gCAAgC,EACrC,KAAK,wBAAwB,EAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,2CAA2C,EACjD,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EAEzB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,2BAA2B,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAClF,YAAY,CAAC,OAAO,CAAC,EAAE,gCAAgC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC3H,QAAQ,CAAC,OAAO,CAAC,EAAE,gCAAgC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAC5H;AAED,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACpE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;CACxC;AAED,MAAM,WAAW,sCAAsC;IACrD,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,KAAK,CAAC,EAAE,2CAA2C,CAAC;IACpD,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,cAAc,CAAC,EAAE,wBAAwB,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sCAAsC,GAC9C,yBAAyB,CAqD3B"}
package/dist/storage.js CHANGED
@@ -65,6 +65,7 @@ __export(exports_config, {
65
65
  TODOS_STORAGE_FALLBACK_ENV: () => TODOS_STORAGE_FALLBACK_ENV,
66
66
  TODOS_STORAGE_ENV: () => TODOS_STORAGE_ENV,
67
67
  STORAGE_TABLES: () => STORAGE_TABLES,
68
+ REMOVED_STORAGE_MODE_ENV_KEYS: () => REMOVED_STORAGE_MODE_ENV_KEYS,
68
69
  CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV: () => CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV,
69
70
  CANONICAL_TODOS_RDS_DATABASE: () => CANONICAL_TODOS_RDS_DATABASE,
70
71
  CANONICAL_TODOS_RDS_CLUSTER_ENV: () => CANONICAL_TODOS_RDS_CLUSTER_ENV
@@ -126,25 +127,36 @@ function assertTodosRemoteStorageConfig(config) {
126
127
  if (!isTodosPostgresBackend(config))
127
128
  return;
128
129
  if (!config.database?.url) {
129
- throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when ${TODOS_STORAGE_ENV.mode}=${config.mode}`);
130
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when the postgresql backend is selected`);
130
131
  }
131
132
  }
132
133
  function parseStorageBackend(value) {
133
134
  const normalized = clean(value)?.toLowerCase();
134
135
  if (!normalized)
135
136
  return "sqlite";
136
- if (normalized === "sqlite" || normalized === "postgres")
137
- return normalized;
138
- const legacy = LEGACY_BACKEND_TOKENS[normalized];
139
- if (legacy)
140
- return legacy;
141
- throw new Error(`${TODOS_STORAGE_ENV.mode} must be sqlite or postgres (legacy values local and remote are accepted)`);
137
+ if (normalized === "sqlite")
138
+ return "sqlite";
139
+ if (normalized === "postgres" || normalized === "postgresql")
140
+ return "postgres";
141
+ if (["local", "remote", "cloud", "hybrid", "self_hosted"].includes(normalized)) {
142
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} selects the backend. Deployment modes no longer exist: ` + `delete the storage-mode variable and set ${TODOS_STORAGE_ENV.databaseUrl} to select the ` + `postgresql backend, or leave it unset for sqlite.`);
143
+ }
144
+ throw new Error(`Storage backend must be sqlite or postgres`);
142
145
  }
143
146
  function parseStorageMode(value) {
144
147
  return parseStorageBackend(value);
145
148
  }
146
149
  function getTodosStorageBackend(env = process.env) {
147
- return parseStorageBackend(readStorageEnv(env, "mode").value);
150
+ for (const key of REMOVED_STORAGE_MODE_ENV_KEYS) {
151
+ if (Object.hasOwn(env, key) && env[key] !== undefined) {
152
+ throw new Error(`${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `Set ${TODOS_STORAGE_ENV.databaseUrl} to select the postgresql backend, ` + `or leave it unset for sqlite.`);
153
+ }
154
+ }
155
+ if (!getTodosStorageDatabaseUrl(env))
156
+ return "sqlite";
157
+ if (isTodosShadowEnabled(env))
158
+ return "sqlite";
159
+ return "postgres";
148
160
  }
149
161
  function getTodosStorageMode(env = process.env) {
150
162
  return getTodosStorageBackend(env);
@@ -218,7 +230,7 @@ function parsePositiveInteger(value, fallback) {
218
230
  }
219
231
  return parsed;
220
232
  }
221
- var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos", LEGACY_BACKEND_TOKENS;
233
+ var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, REMOVED_STORAGE_MODE_ENV_KEYS, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos";
222
234
  var init_config = __esm(() => {
223
235
  TODOS_STORAGE_TABLES = [
224
236
  "todos_sync_records",
@@ -226,7 +238,6 @@ var init_config = __esm(() => {
226
238
  ];
227
239
  STORAGE_TABLES = TODOS_STORAGE_TABLES;
228
240
  TODOS_STORAGE_ENV = {
229
- mode: "HASNA_TODOS_STORAGE_MODE",
230
241
  shadow: "HASNA_TODOS_SHADOW",
231
242
  databaseUrl: "HASNA_TODOS_DATABASE_URL",
232
243
  databaseSsl: "HASNA_TODOS_DATABASE_SSL",
@@ -243,7 +254,6 @@ var init_config = __esm(() => {
243
254
  syncDryRun: "HASNA_TODOS_SYNC_DRY_RUN"
244
255
  };
245
256
  TODOS_STORAGE_FALLBACK_ENV = {
246
- mode: "TODOS_STORAGE_MODE",
247
257
  shadow: "TODOS_SHADOW",
248
258
  databaseUrl: "TODOS_DATABASE_URL",
249
259
  databaseSsl: "TODOS_DATABASE_SSL",
@@ -259,14 +269,12 @@ var init_config = __esm(() => {
259
269
  syncBatchSize: "TODOS_SYNC_BATCH_SIZE",
260
270
  syncDryRun: "TODOS_SYNC_DRY_RUN"
261
271
  };
262
- LEGACY_BACKEND_TOKENS = {
263
- local: "sqlite",
264
- remote: "postgres",
265
- postgresql: "postgres",
266
- hybrid: "postgres",
267
- self_hosted: "postgres",
268
- cloud: "postgres"
269
- };
272
+ REMOVED_STORAGE_MODE_ENV_KEYS = [
273
+ "HASNA_TODOS_STORAGE_MODE",
274
+ "HASNA_TODOS_MODE",
275
+ "TODOS_STORAGE_MODE",
276
+ "TODOS_MODE"
277
+ ];
270
278
  });
271
279
 
272
280
  // src/types/index.ts
@@ -2327,6 +2335,10 @@ var init_migrations = __esm(() => {
2327
2335
  );
2328
2336
  INSERT OR IGNORE INTO _migrations (id) VALUES (70);
2329
2337
  COMMIT;
2338
+ `,
2339
+ `BEGIN;
2340
+ INSERT OR IGNORE INTO _migrations (id) VALUES (71);
2341
+ COMMIT;
2330
2342
  `
2331
2343
  ];
2332
2344
  });
@@ -3000,6 +3012,8 @@ function ensureSchema(db) {
3000
3012
  ensureColumn("projects", "task_list_id", "TEXT");
3001
3013
  ensureColumn("projects", "task_prefix", "TEXT");
3002
3014
  ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
3015
+ ensureColumn("projects", "parent_id", "TEXT REFERENCES projects(id) ON DELETE SET NULL");
3016
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_projects_parent_id ON projects(parent_id)");
3003
3017
  ensureColumn("tasks", "plan_id", "TEXT REFERENCES plans(id) ON DELETE SET NULL");
3004
3018
  ensureColumn("tasks", "task_list_id", "TEXT REFERENCES task_lists(id) ON DELETE SET NULL");
3005
3019
  ensureColumn("tasks", "short_id", "TEXT");
@@ -5037,17 +5051,24 @@ function createProject(input, db) {
5037
5051
  const id = uuid();
5038
5052
  const timestamp2 = now();
5039
5053
  const derivedSlug = slugify(input.name);
5040
- const taskListId = input.task_list_id === undefined ? `todos-${derivedSlug}` : slugify(input.task_list_id);
5054
+ const taskListId = input.task_list_id === undefined ? derivedSlug : slugify(input.task_list_id);
5041
5055
  if (!derivedSlug || !taskListId)
5042
5056
  throw new Error("Project name and task-list slug must be non-empty");
5043
5057
  const slugConflict = d.query("SELECT id FROM projects WHERE task_list_id = ? LIMIT 1").get(taskListId);
5044
5058
  if (slugConflict || !claimCanonicalSlug("project", "global", taskListId, id, d)) {
5045
5059
  throw new ResourceConflictError("PROJECT_SLUG_CONFLICT", `Project slug "${taskListId}" already exists`);
5046
5060
  }
5061
+ const parentId = input.parent_id ?? null;
5062
+ if (parentId !== null) {
5063
+ const parent = getProject(parentId, d);
5064
+ if (!parent)
5065
+ throw new ProjectNotFoundError(parentId);
5066
+ assertNotProjectAncestor(id, parentId, d);
5067
+ }
5047
5068
  const taskPrefix = input.task_prefix || generatePrefix(input.name, d);
5048
5069
  const machineId = currentStorageMachineId(d);
5049
- d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, created_at, updated_at, machine_id)
5050
- VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, timestamp2, timestamp2, machineId]);
5070
+ d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, parent_id, created_at, updated_at, machine_id)
5071
+ VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, parentId, timestamp2, timestamp2, machineId]);
5051
5072
  return getProject(id, d);
5052
5073
  })();
5053
5074
  }
@@ -5072,6 +5093,60 @@ function listProjects(db) {
5072
5093
  const d = db || getDatabase();
5073
5094
  return d.query("SELECT * FROM projects ORDER BY name").all();
5074
5095
  }
5096
+ function orderProjectsParentFirst(projects) {
5097
+ const projectId = (project) => {
5098
+ const id = project.id;
5099
+ return typeof id === "string" && id.length > 0 ? id : null;
5100
+ };
5101
+ const parentId = (project) => {
5102
+ const parent = project.parent_id;
5103
+ return parent == null ? null : String(parent);
5104
+ };
5105
+ const byId = new Set;
5106
+ for (const project of projects) {
5107
+ const id = projectId(project);
5108
+ if (id !== null)
5109
+ byId.add(id);
5110
+ }
5111
+ const ordered = [];
5112
+ const emitted = new Set;
5113
+ let remaining = [...projects];
5114
+ let progress = true;
5115
+ while (progress && remaining.length > 0) {
5116
+ progress = false;
5117
+ const deferred = [];
5118
+ for (const project of remaining) {
5119
+ const parent = parentId(project);
5120
+ if (parent === null || emitted.has(parent) || !byId.has(parent)) {
5121
+ ordered.push(project);
5122
+ const id = projectId(project);
5123
+ if (id !== null)
5124
+ emitted.add(id);
5125
+ progress = true;
5126
+ } else {
5127
+ deferred.push(project);
5128
+ }
5129
+ }
5130
+ remaining = deferred;
5131
+ }
5132
+ ordered.push(...remaining);
5133
+ return ordered;
5134
+ }
5135
+ function assertNotProjectAncestor(projectId, ancestorId, db) {
5136
+ const d = db || getDatabase();
5137
+ let cursor = ancestorId;
5138
+ const seen = new Set;
5139
+ while (cursor !== null) {
5140
+ if (cursor === projectId) {
5141
+ throw new ResourceConflictError("PROJECT_PARENT_CYCLE", `Project "${projectId}" cannot be placed under its own descendant`);
5142
+ }
5143
+ if (seen.has(cursor))
5144
+ break;
5145
+ seen.add(cursor);
5146
+ const row = d.query("SELECT parent_id FROM projects WHERE id = ?").get(cursor);
5147
+ cursor = row?.parent_id ?? null;
5148
+ }
5149
+ }
5075
5150
  function updateProject(id, input, db) {
5076
5151
  const d = db || getDatabase();
5077
5152
  const project = getProject(id, d);
@@ -5094,6 +5169,16 @@ function updateProject(id, input, db) {
5094
5169
  sets.push("path = ?");
5095
5170
  params.push(input.path);
5096
5171
  }
5172
+ if (input.parent_id !== undefined) {
5173
+ if (input.parent_id !== null) {
5174
+ const parent = getProject(input.parent_id, d);
5175
+ if (!parent)
5176
+ throw new ProjectNotFoundError(input.parent_id);
5177
+ assertNotProjectAncestor(id, input.parent_id, d);
5178
+ }
5179
+ sets.push("parent_id = ?");
5180
+ params.push(input.parent_id);
5181
+ }
5097
5182
  params.push(id);
5098
5183
  d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
5099
5184
  return getProject(id, d);
@@ -7227,8 +7312,8 @@ var init_secret_redaction = __esm(() => {
7227
7312
  SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
7228
7313
  DEFAULT_PATTERNS = [
7229
7314
  { name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
7230
- { name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
7231
- { name: "github_oauth", pattern: /\bgho_[a-zA-Z0-9]{20,}\b/g },
7315
+ { name: "github_pat", pattern: /\bgh[p]_[a-zA-Z0-9]{20,}\b/g },
7316
+ { name: "github_oauth", pattern: /\bgh[o]_[a-zA-Z0-9]{20,}\b/g },
7232
7317
  { name: "aws_access_key", pattern: /\bAKIA[0-9A-Z]{16}\b/g },
7233
7318
  { name: "bearer_token", pattern: /\bBearer\s+[a-zA-Z0-9\-._~+/]+=*\b/gi },
7234
7319
  { name: "jwt", pattern: /\beyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },
@@ -7240,7 +7325,7 @@ var init_secret_redaction = __esm(() => {
7240
7325
  /example\.com/i,
7241
7326
  /your-api-key-here/i,
7242
7327
  /sk-test/i,
7243
- /ghp_xxx/i
7328
+ /gh[p]_xxx/i
7244
7329
  ];
7245
7330
  customRedactors = [];
7246
7331
  });
@@ -14083,6 +14168,7 @@ var PROJECT_COLUMNS = [
14083
14168
  "task_list_id",
14084
14169
  "task_prefix",
14085
14170
  "task_counter",
14171
+ "parent_id",
14086
14172
  "created_at",
14087
14173
  "updated_at",
14088
14174
  "machine_id",
@@ -14302,7 +14388,7 @@ function importSqliteTodosStorageSnapshot(snapshot, db) {
14302
14388
  }
14303
14389
  }
14304
14390
  };
14305
- applyRows("projects", "projects", PROJECT_COLUMNS, snapshot.projects, "updated_at");
14391
+ applyRows("projects", "projects", PROJECT_COLUMNS, orderProjectsParentFirst(snapshot.projects), "updated_at");
14306
14392
  applyRows("project_machine_paths", "project_machine_paths", PROJECT_MACHINE_PATH_COLUMNS, snapshot.projectMachinePaths ?? [], "updated_at");
14307
14393
  applyRows("agents", "agents", AGENT_COLUMNS, snapshot.agents, "last_seen_at");
14308
14394
  applyRows("task_lists", "task_lists", TASK_LIST_COLUMNS, snapshot.taskLists, "updated_at");
@@ -15757,8 +15843,12 @@ class PostgresJsonRecordStore {
15757
15843
  OR ($11::text <> $2
15758
15844
  AND NOT EXISTS (SELECT 1 FROM parent_chain WHERE object_id = $2)
15759
15845
  AND NOT EXISTS (SELECT 1 FROM parent_chain WHERE cycle))) AS parent_acyclic,
15760
- (SELECT count(*) FROM locked_plans) = jsonb_array_length($7::jsonb) AS all_plans_found,
15761
- ($8::text IS NULL OR EXISTS (SELECT 1 FROM locked_plans WHERE object_id = $8)) AS target_plan_found,
15846
+ (COALESCE((SELECT payload->>'plan_id' FROM locked_task), '')
15847
+ IS DISTINCT FROM COALESCE($3::jsonb->>'plan_id', '')) AS membership_changed,
15848
+ (NOT (COALESCE((SELECT payload->>'plan_id' FROM locked_task), '')
15849
+ IS DISTINCT FROM COALESCE($3::jsonb->>'plan_id', ''))
15850
+ OR $8::text IS NULL
15851
+ OR EXISTS (SELECT 1 FROM locked_plans WHERE object_id = $8)) AS target_plan_found,
15762
15852
  (SELECT payload->>'project_id' FROM locked_plans WHERE object_id = $8) AS target_project_id
15763
15853
  ), guarded AS (
15764
15854
  SELECT
@@ -15781,7 +15871,6 @@ class PostgresJsonRecordStore {
15781
15871
  AND guarded.version_matches
15782
15872
  AND guarded.parent_found
15783
15873
  AND guarded.parent_acyclic
15784
- AND guarded.all_plans_found
15785
15874
  AND guarded.target_plan_found
15786
15875
  AND NOT guarded.project_conflict
15787
15876
  ON CONFLICT (service, object_type, object_id) DO UPDATE SET
@@ -15797,7 +15886,7 @@ class PostgresJsonRecordStore {
15797
15886
  RETURNING payload
15798
15887
  )
15799
15888
  SELECT guarded.task_found, guarded.version_matches, guarded.parent_found, guarded.parent_acyclic,
15800
- guarded.all_plans_found, guarded.target_plan_found, guarded.project_conflict,
15889
+ guarded.membership_changed, guarded.target_plan_found, guarded.project_conflict,
15801
15890
  (SELECT payload FROM stored) AS payload,
15802
15891
  (SELECT payload FROM locked_task) AS current_payload
15803
15892
  FROM guarded`, [
@@ -15829,7 +15918,7 @@ class PostgresJsonRecordStore {
15829
15918
  if (parentGuard && !row?.parent_acyclic && parentGuard.parentId) {
15830
15919
  throw new ResourceConflictError("TASK_PARENT_CYCLE", `TASK_PARENT_CYCLE: assigning parent ${parentGuard.parentId} to task ${value.id} would create or retain a parent cycle`);
15831
15920
  }
15832
- if (!row?.all_plans_found || !row.target_plan_found) {
15921
+ if (!row?.target_plan_found) {
15833
15922
  throw new PlanProjectLinkError("PLAN_PROJECT_LINK_PLAN_NOT_FOUND", `Plan membership changed through a missing plan: ${targetPlanId ?? planIds.join(", ")}`, { plan_ids: planIds, target_plan_id: targetPlanId });
15834
15923
  }
15835
15924
  if (row.project_conflict) {
@@ -17041,17 +17130,26 @@ async function getChangedSince(since, filters, store) {
17041
17130
  async function createProject2(input, store, context) {
17042
17131
  const timestamp2 = new Date().toISOString();
17043
17132
  const derivedSlug = slugifyRaw(input.name);
17044
- const taskListId = input.task_list_id === undefined ? `todos-${derivedSlug}` : slugifyRaw(input.task_list_id);
17133
+ const taskListId = input.task_list_id === undefined ? derivedSlug : slugifyRaw(input.task_list_id);
17045
17134
  if (!derivedSlug || !taskListId)
17046
17135
  throw new Error("Project name and task-list slug must be non-empty");
17136
+ const parentId = input.parent_id ?? null;
17137
+ const id = randomUUID3();
17138
+ if (parentId !== null) {
17139
+ const parent = await store.get("projects", parentId);
17140
+ if (!parent)
17141
+ throw new ProjectNotFoundError(parentId);
17142
+ await assertNotProjectAncestorPostgres(id, parentId, store);
17143
+ }
17047
17144
  const project = {
17048
- id: randomUUID3(),
17145
+ id,
17049
17146
  name: input.name,
17050
17147
  path: input.path,
17051
17148
  description: input.description ?? null,
17052
17149
  task_list_id: taskListId,
17053
17150
  task_prefix: input.task_prefix ?? await generateProjectPrefix(input.name, store),
17054
17151
  task_counter: 0,
17152
+ parent_id: parentId,
17055
17153
  created_at: timestamp2,
17056
17154
  updated_at: timestamp2,
17057
17155
  machine_id: store.machineId(context),
@@ -17059,12 +17157,38 @@ async function createProject2(input, store, context) {
17059
17157
  };
17060
17158
  return store.upsert("projects", project, context);
17061
17159
  }
17160
+ async function assertNotProjectAncestorPostgres(projectId, candidateParentId, store) {
17161
+ const all = await store.list("projects");
17162
+ const byId = new Map(all.map((project) => [project.id, project.parent_id ?? null]));
17163
+ let cursor = candidateParentId;
17164
+ const seen = new Set;
17165
+ while (cursor !== null) {
17166
+ if (cursor === projectId) {
17167
+ throw new ResourceConflictError("PROJECT_PARENT_CYCLE", `Project "${projectId}" cannot be placed under its own descendant`);
17168
+ }
17169
+ if (seen.has(cursor))
17170
+ break;
17171
+ seen.add(cursor);
17172
+ cursor = byId.get(cursor) ?? null;
17173
+ }
17174
+ }
17062
17175
  async function updateProject2(id, input, store) {
17063
17176
  if ("task_list_id" in input) {
17064
17177
  throw new Error("task_list_id cannot be changed by updateProject; use renameProject for an atomic canonical rename");
17065
17178
  }
17066
17179
  const project = await requireRecord("projects", id, store);
17067
- const updated = { ...project, ...definedPatch(input), updated_at: new Date().toISOString() };
17180
+ if (input.parent_id !== undefined && input.parent_id !== null) {
17181
+ const parent = await store.get("projects", input.parent_id);
17182
+ if (!parent)
17183
+ throw new ProjectNotFoundError(input.parent_id);
17184
+ await assertNotProjectAncestorPostgres(id, input.parent_id, store);
17185
+ }
17186
+ const updated = {
17187
+ ...project,
17188
+ ...definedPatch(input),
17189
+ ...input.parent_id !== undefined ? { parent_id: input.parent_id } : {},
17190
+ updated_at: new Date().toISOString()
17191
+ };
17068
17192
  return store.upsert("projects", updated);
17069
17193
  }
17070
17194
  async function createPlan2(input, store, context) {
@@ -749,8 +749,8 @@ var init_secret_redaction = __esm(() => {
749
749
  SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
750
750
  DEFAULT_PATTERNS = [
751
751
  { name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
752
- { name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
753
- { name: "github_oauth", pattern: /\bgho_[a-zA-Z0-9]{20,}\b/g },
752
+ { name: "github_pat", pattern: /\bgh[p]_[a-zA-Z0-9]{20,}\b/g },
753
+ { name: "github_oauth", pattern: /\bgh[o]_[a-zA-Z0-9]{20,}\b/g },
754
754
  { name: "aws_access_key", pattern: /\bAKIA[0-9A-Z]{16}\b/g },
755
755
  { name: "bearer_token", pattern: /\bBearer\s+[a-zA-Z0-9\-._~+/]+=*\b/gi },
756
756
  { name: "jwt", pattern: /\beyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },
@@ -762,7 +762,7 @@ var init_secret_redaction = __esm(() => {
762
762
  /example\.com/i,
763
763
  /your-api-key-here/i,
764
764
  /sk-test/i,
765
- /ghp_xxx/i
765
+ /gh[p]_xxx/i
766
766
  ];
767
767
  customRedactors = [];
768
768
  });
package/dist/testing.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * so any test that spawns the CLI with `{ ...process.env }`, or loads the SDK in-process,
8
8
  * writes into the **shared hosted store**. That is not hypothetical:
9
9
  *
10
- * - `hasnaxyz/iapp-takumi` test suites leaked **1,151** rows (`seed-task-<epoch>`,
10
+ * - a consumer test suite leaked **1,151** rows (`seed-task-<epoch>`,
11
11
  * `Short ID resolution test`, …) between 2026-04-06 and 2026-06-21.
12
12
  * - `hasna/open-loops` `drain.test.ts` leaked **943** `Merge the release PR` rows
13
13
  * between 2026-07-05 and 2026-07-15.
@@ -48,17 +48,26 @@ export declare const SHARED_TODOS_STORE_ENV_KEYS: readonly ["HASNA_TODOS_API_URL
48
48
  * destination. They are pinned rather than blanked, so they are exempt from the scrub
49
49
  * coverage assertion.
50
50
  */
51
- export declare const LOCAL_ONLY_TODOS_ENV_KEYS: readonly ["HASNA_TODOS_STORAGE_MODE", "TODOS_STORAGE_MODE", "HASNA_TODOS_DB_PATH", "TODOS_DB_PATH"];
51
+ export declare const LOCAL_ONLY_TODOS_ENV_KEYS: readonly ["HASNA_TODOS_DB_PATH", "TODOS_DB_PATH"];
52
+ /**
53
+ * The retired storage-mode variables (owner directive 2026-08-15). They are
54
+ * banned: if any is present the resolver THROWS, so a test env must DELETE them,
55
+ * never blank them — a blank leftover is still a stale fragment. They are listed
56
+ * here so the scrub-coverage assertion treats them as handled.
57
+ */
58
+ export declare const REMOVED_TODOS_ENV_KEYS: readonly ["HASNA_TODOS_STORAGE_MODE", "HASNA_TODOS_MODE", "TODOS_STORAGE_MODE", "TODOS_MODE"];
52
59
  export type SharedTodosStoreEnvKey = (typeof SHARED_TODOS_STORE_ENV_KEYS)[number];
53
60
  export type TodosTestEnv = Record<string, string | undefined>;
54
61
  /**
55
- * `process.env` with every shared-store pointer blanked and storage pinned to a local
56
- * SQLite file. Overrides are applied last, so a test that deliberately exercises the
62
+ * `process.env` with every shared-store pointer blanked and the retired
63
+ * storage-mode variables deleted, so the transport resolves to a local SQLite
64
+ * file. Overrides are applied last, so a test that deliberately exercises the
57
65
  * hosted transport against a throwaway server can still opt back in explicitly.
58
66
  *
59
- * Blank string, not `delete`: `resolveTodosCliStorageMode` treats a blank
60
- * `*_STORAGE_MODE` as invalid and throws rather than silently falling back, so a
61
- * half-configured child fails loudly instead of guessing.
67
+ * Blank string, not `delete`, for the shared-store pointers: a blanked URL/key
68
+ * is treated as absent, and there is no partial-config fallback. The retired
69
+ * `*_STORAGE_MODE` variables are DELETED, not blanked, because their mere
70
+ * presence is a hard error.
62
71
  */
63
72
  export declare function localTodosTestEnv(overrides?: TodosTestEnv): TodosTestEnv;
64
73
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,qNAa9B,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,qGAK5B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAElF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,YAAiB,GAAG,YAAY,CAM5E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,GAAE,YAAiB,GAAG,MAAM,IAAI,CAqB/E;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,YAA0C,GAAG,IAAI,CAQ7F"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,qNAa9B,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,mDAG5B,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,+FAKzB,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAElF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9D;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,YAAiB,GAAG,YAAY,CAK5E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,GAAE,YAAiB,GAAG,MAAM,IAAI,CAoB/E;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,YAA0C,GAAG,IAAI,CAQ7F"}