@odla-ai/cli 0.27.3 → 0.27.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -501,6 +501,8 @@ interface OdlaProjectConfig {
501
501
  publicRead?: string[];
502
502
  };
503
503
  ai?: {
504
+ /** Hosted is the default when omitted; provider-only legacy configs are BYOK. */
505
+ mode?: "hosted" | "byok";
504
506
  provider?: string;
505
507
  model?: string;
506
508
  /** Env var containing the provider key to store in the platform vault. */
@@ -829,7 +831,7 @@ interface DoctorOptions {
829
831
  * It then collects warnings for the mistakes provision can't fix silently: a
830
832
  * schema with no entities, a rules namespace with no matching entity, an
831
833
  * integration with incomplete rules or a seed targeting an unknown namespace, the `ai`
832
- * service enabled without an `ai.provider`, `auth.clerk` entries that reference
834
+ * BYOK service enabled without an `ai.provider`, `auth.clerk` entries that reference
833
835
  * an unset env var (`$FOO`), an `ai.keyEnv` that isn't set (provider key upload
834
836
  * will be skipped), and — when `o11y` is enabled — any env whose local
835
837
  * credentials lack an ingest token. It also folds in the shared rule linter,
@@ -864,9 +866,9 @@ interface InitOptions {
864
866
  * written when it doesn't already exist unless `force` is set (otherwise it
865
867
  * throws); the schema/rules files are written only when missing (never
866
868
  * overwritten, even with `force`). `appId` must match `^[a-z0-9][a-z0-9-]*$` or
867
- * it throws. Defaults: `envs` → `["dev"]`, `services` → `["db","ai"]`,
868
- * `aiProvider` `"anthropic"` (which also picks the `keyEnv`, e.g.
869
- * `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`/`GOOGLE_API_KEY`).
869
+ * it throws. Defaults: `envs` → `["dev"]`, `services` → `["db","ai"]`, and
870
+ * platform-hosted AI. Passing `aiProvider` explicitly opts into BYOK and also
871
+ * picks its `keyEnv`.
870
872
  *
871
873
  * @param options.appId Slug for the app (lowercase alphanumerics and hyphens).
872
874
  * @param options.name Human-readable app name embedded in the config.
@@ -874,7 +876,7 @@ interface InitOptions {
874
876
  * @param options.configPath Config filename relative to root (default `odla.config.mjs`).
875
877
  * @param options.envs Environment names (default `["dev"]`; production is an explicit opt-in).
876
878
  * @param options.services Enabled services (default `["db","ai"]`).
877
- * @param options.aiProvider AI provider slug (default `"anthropic"`).
879
+ * @param options.aiProvider Optional provider slug; supplying it opts into BYOK.
878
880
  * @param options.force Overwrite an existing config file instead of throwing.
879
881
  * @param options.stdout Optional console-like sink (defaults to `console`).
880
882
  */
package/dist/index.d.ts CHANGED
@@ -501,6 +501,8 @@ interface OdlaProjectConfig {
501
501
  publicRead?: string[];
502
502
  };
503
503
  ai?: {
504
+ /** Hosted is the default when omitted; provider-only legacy configs are BYOK. */
505
+ mode?: "hosted" | "byok";
504
506
  provider?: string;
505
507
  model?: string;
506
508
  /** Env var containing the provider key to store in the platform vault. */
@@ -829,7 +831,7 @@ interface DoctorOptions {
829
831
  * It then collects warnings for the mistakes provision can't fix silently: a
830
832
  * schema with no entities, a rules namespace with no matching entity, an
831
833
  * integration with incomplete rules or a seed targeting an unknown namespace, the `ai`
832
- * service enabled without an `ai.provider`, `auth.clerk` entries that reference
834
+ * BYOK service enabled without an `ai.provider`, `auth.clerk` entries that reference
833
835
  * an unset env var (`$FOO`), an `ai.keyEnv` that isn't set (provider key upload
834
836
  * will be skipped), and — when `o11y` is enabled — any env whose local
835
837
  * credentials lack an ingest token. It also folds in the shared rule linter,
@@ -864,9 +866,9 @@ interface InitOptions {
864
866
  * written when it doesn't already exist unless `force` is set (otherwise it
865
867
  * throws); the schema/rules files are written only when missing (never
866
868
  * overwritten, even with `force`). `appId` must match `^[a-z0-9][a-z0-9-]*$` or
867
- * it throws. Defaults: `envs` → `["dev"]`, `services` → `["db","ai"]`,
868
- * `aiProvider` `"anthropic"` (which also picks the `keyEnv`, e.g.
869
- * `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`/`GOOGLE_API_KEY`).
869
+ * it throws. Defaults: `envs` → `["dev"]`, `services` → `["db","ai"]`, and
870
+ * platform-hosted AI. Passing `aiProvider` explicitly opts into BYOK and also
871
+ * picks its `keyEnv`.
870
872
  *
871
873
  * @param options.appId Slug for the app (lowercase alphanumerics and hyphens).
872
874
  * @param options.name Human-readable app name embedded in the config.
@@ -874,7 +876,7 @@ interface InitOptions {
874
876
  * @param options.configPath Config filename relative to root (default `odla.config.mjs`).
875
877
  * @param options.envs Environment names (default `["dev"]`; production is an explicit opt-in).
876
878
  * @param options.services Enabled services (default `["db","ai"]`).
877
- * @param options.aiProvider AI provider slug (default `"anthropic"`).
879
+ * @param options.aiProvider Optional provider slug; supplying it opts into BYOK.
878
880
  * @param options.force Overwrite an existing config file instead of throwing.
879
881
  * @param options.stdout Optional console-like sink (defaults to `console`).
880
882
  */
package/dist/index.js CHANGED
@@ -57,7 +57,7 @@ import {
57
57
  startHostedSecurityJob,
58
58
  surfacePaths,
59
59
  validateInvocation
60
- } from "./chunk-JWEBGIBR.js";
60
+ } from "./chunk-DQOJ4S6H.js";
61
61
  export {
62
62
  AGENT_HARNESSES,
63
63
  CAPABILITIES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@odla-ai/cli",
3
- "version": "0.27.3",
3
+ "version": "0.27.5",
4
4
  "description": "Agent-operable CLI for odla provisioning, calendar consent and sync lifecycle, System AI administration, Worker secrets, security jobs, and smoke checks.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://odla.ai/docs/packages/cli",
@@ -10,6 +10,7 @@ runbookOrder:
10
10
  - references/build.md
11
11
  - references/sdks.md
12
12
  - references/pm.md
13
+ - references/agent-identity.md
13
14
  - references/co-owners.md
14
15
  ---
15
16
 
@@ -151,19 +152,39 @@ on the app. It is platform-side: there is nothing PM-specific to install or
151
152
  provision. Start a session by reading it, and keep it current while you build:
152
153
 
153
154
  ```cmd
154
- npx @odla-ai/cli pm task list --app <appId> --column doing
155
+ npx @odla-ai/cli pm next --app <appId>
155
156
  ```
156
157
 
158
+ For a durable supervisor loop, establish and persist an exact-project PM cursor:
159
+
160
+ ```cmd
161
+ npx @odla-ai/cli pm watch --app <appId> --entity task --jsonl
162
+ ```
163
+
164
+ A Ready event is a wake-up signal, not permission: reload the task and linked
165
+ goal and win the current revision's atomic claim before starting. PM-attached
166
+ comments use the same cursor (`--action comment.created`); general Discussion
167
+ topics and mentions still use `discuss watch`.
168
+
157
169
  The app must already be registered for ownership-scoped PM access. On a new
158
170
  project, use a focused commit/checkpoint only until the first provision, then
159
171
  initialize PM and backfill the approved early evidence immediately. Across
160
172
  several efforts, list without `--app` first, then scope every write to the exact
161
173
  project id.
162
174
 
163
- Record a **decision** the moment you make one (with what it rules out), file a
164
- **bug** the moment you notice one, and move a **task** when the work moves. A
165
- decision you don't record is one the next agent re-litigates. Full command set,
166
- conventions, and the read-first session opener: `references/pm.md`.
175
+ Before project-mutating work, confirm the request aligns to an open goal and
176
+ propose/refine a **task** in Backlog. Ready is a planner handoff: an ordinary
177
+ managed-agent grant has `pm.execute`, so a human owner or `pm.plan` agent must
178
+ approve the Ready contract before the executor claims its reviewed revision.
179
+ Record a **decision** the moment you make one (with what it rules out), and file
180
+ a **bug** the moment you notice one. A decision you don't record is one the next
181
+ agent re-litigates. Full command set, conventions, and recovery procedure:
182
+ `references/pm.md` and `references/pm-work-intake.md`.
183
+
184
+ For an agent that spans linked worktrees or customer repositories, follow
185
+ `references/agent-identity.md`: select a named context and verify the same
186
+ immutable, project-derived handle is requested and human-approved when
187
+ reconnecting an expired credential.
167
188
 
168
189
  ## Operational runbooks live in the database, not in this bundle
169
190
 
@@ -289,6 +310,9 @@ code, paste a publishable key, run a command) — and wait for a nod.
289
310
  - `references/pm.md` — the project manager for agents: goals, board, decisions,
290
311
  bugs. What to file where, the `pm` command set, and how to open and close a
291
312
  session against it.
313
+ - `references/agent-identity.md` — preserve one attributable agent principal
314
+ across credential expiry, linked worktrees, and non-odla-ai projects without
315
+ copying a token or widening project authority.
292
316
  - `references/co-owners.md` — sharing one app's db and tooling across a team:
293
317
  `app owners add/list/remove`, and how each co-owner self-provisions their own
294
318
  credentials (dev and the shared prod database) without any secret handoff.
@@ -0,0 +1,125 @@
1
+ # Reconnect an agent across worktrees and projects
2
+
3
+ Use this procedure when an agent must keep the same attributable identity while
4
+ its short-lived device credential expires, or when it operates outside the
5
+ checkout that first obtained the credential.
6
+
7
+ ## Preconditions
8
+
9
+ - The human approving the request owns or co-owns every selected project.
10
+ - The handshake contains one deliberate handle suffix, such as `customer-app`
11
+ or `release-reviewer`. The Registry places it under the approving human's
12
+ verified namespace (for example, `@cory/customer-app`). The CLI derives this
13
+ suffix deterministically from the exact project; SDK callers may explicitly
14
+ request a different stable `agentHandle` for a cross-project agent.
15
+ - No token, credential file, or browser session is copied between worktrees,
16
+ machines, people, or chat messages.
17
+
18
+ ## 1. Save non-secret context outside the checkout
19
+
20
+ ```cmd
21
+ npx @odla-ai/cli context save delivery --platform https://odla.ai --app <appId> --env prod
22
+ npx @odla-ai/cli context show --context delivery --json
23
+ ```
24
+
25
+ The context stores only platform/app/environment metadata. Its private token
26
+ cache lives under `~/.odla/profiles/delivery/`, independently of any repository
27
+ or linked worktree. Select it explicitly with `--context delivery` or
28
+ `ODLA_CONTEXT=delivery`; there is no ambient current context.
29
+
30
+ ## 2. Establish the durable principal
31
+
32
+ Run the intended command with the named context. If no valid cached credential
33
+ exists, the CLI starts the normal email-bound device handshake and prints the
34
+ exact Studio approval URL.
35
+
36
+ ```cmd
37
+ npx @odla-ai/cli whoami --context delivery --email <account-email> --json
38
+ ```
39
+
40
+ In Studio, review the exact code, requested handle, projects, and capabilities.
41
+ The request is immutable: approval cannot edit its identity or grants. The
42
+ handle—not the request label or local context name—is the durable agent identity
43
+ key. Record the full verified handle in the agent handoff; never record the
44
+ token. A normal CLI handshake derives the same handle from the configured
45
+ project in every checkout and worktree.
46
+
47
+ After approval and collection, verify the selected identity and context:
48
+
49
+ ```cmd
50
+ npx @odla-ai/cli whoami --context delivery --json
51
+ npx @odla-ai/cli pm next --context delivery --json
52
+ ```
53
+
54
+ If the agent request omits the handle, Registry deliberately creates a new
55
+ principal for that handshake. An untrusted request label can therefore never
56
+ select an existing agent.
57
+
58
+ ## 3. Reconnect after expiry
59
+
60
+ Re-run the command with the same named context and project. The cache keeps the
61
+ approved account identifier and starts a fresh handshake when the token is
62
+ expired. During human review:
63
+
64
+ 1. verify the immutable request contains the expected existing handle suffix;
65
+ 2. verify every exact requested project should receive access now;
66
+ 3. review optional capabilities again; and
67
+ 4. approve the request unchanged, or decline it with corrective feedback.
68
+
69
+ Registry binds the new credential to the existing managed-agent principal and
70
+ stores the human's reviewed exact-project set without activating it yet. The
71
+ existing principal's authoritative name and purpose are retained; reusing its
72
+ handle is not a profile-edit operation. Approval alone leaves the current
73
+ credential and its project grants usable. When the replacement plaintext is
74
+ successfully collected, Registry atomically clears that plaintext, retires
75
+ older collected ordinary-handshake credentials, and replaces the principal's
76
+ manager-issued project grants with the reviewed set. Newly selected projects
77
+ are unavailable before collection, and omitted projects are revoked during the
78
+ switch. If collection never succeeds, the working credential and grants are not
79
+ stranded by the approval.
80
+
81
+ Verify `whoami` reports the same principal/handle before resuming work. Ordinary
82
+ reviewed project access includes `pm.execute` but not `pm.plan`: re-read `pm
83
+ next` and claim the current Ready task revision, while leaving Ready approval to
84
+ a human owner or explicit planner. Identity continuity alone does not preserve
85
+ a stale claim or imply authority outside the newly reviewed grants.
86
+
87
+ ## 4. Review remaining credentials
88
+
89
+ Successful collection automatically retires the preceding collected ordinary
90
+ handshake credential for that principal. Verify the old row in Studio's
91
+ agent-credential inventory has a `system:handshake-reconnect:<handshake-id>`
92
+ revocation reason. Do not delete a local cache as a substitute for this
93
+ server-side evidence.
94
+
95
+ The rotation set deliberately excludes manually minted credentials and
96
+ separately scoped handshakes: those may represent another deliberate runtime,
97
+ not a stale copy of this context. Review and revoke them explicitly when they
98
+ are no longer intended.
99
+
100
+ Project grants are still time-bounded. Collection makes the newly reviewed
101
+ selection authoritative for the principal: selected projects receive new
102
+ revisions and every live manager-issued project omitted from that selection is
103
+ revoked. There is not yet a non-interactive `auth rotate` command or OS
104
+ credential-store integration. The named context plus stable handle solves
105
+ worktree/project portability and principal continuity, but it is not ambient or
106
+ permanent authority.
107
+
108
+ ## Recovery and audit evidence
109
+
110
+ - `handshake_pending`: approve the existing code and rerun the same command; do
111
+ not create a substitute request.
112
+ - Wrong handle requested: decline it and correct the project/SDK request before
113
+ starting a fresh handshake. If already approved, revoke the new credential in
114
+ Studio; do not rename or merge principals by editing local metadata.
115
+ - Missing project after reconnect: start a new reviewed handshake and select the
116
+ exact project; never widen to all projects.
117
+ - Project ownership/lifecycle changed after approval: collection expires the
118
+ unusable replacement while preserving the current credential and grant set.
119
+ Restore the intended project state, then start and review a fresh handshake.
120
+ - Lost local cache: save/select the same non-secret context, complete a new
121
+ handshake with the same handle, verify `whoami`, then revoke the lost
122
+ credential from Studio.
123
+
124
+ Audit evidence is the stable principal id/handle, the new credential id, and the
125
+ monotonic project-grant revision shown by Registry—not possession of a token.
@@ -0,0 +1,131 @@
1
+ # PM work intake — align, ready, claim, and recover
2
+
3
+ Use this runbook whenever an agent is about to begin project-mutating work.
4
+ PM goals and tasks are the durable records. Discussion is the communication
5
+ channel. A Codex Goal, when the harness supports it, is the execution objective
6
+ for the current chat and should name the PM goal and task references.
7
+
8
+ ## Preconditions
9
+
10
+ - Resolve the exact `appId`; do not infer it from the repository name.
11
+ - Use a task-owned worktree and branch before editing.
12
+ - Never put a credential, token, or secret in PM or Discussion.
13
+ - A question, investigation, or alignment conversation may proceed without a
14
+ claim. Implementation may not.
15
+
16
+ ## 1. Resolve context and read authoritative state
17
+
18
+ ```cmd
19
+ npx @odla-ai/cli context show --context <profile>
20
+ npx @odla-ai/cli pm next --app <appId> --json
21
+ npx @odla-ai/cli pm handoff --app <appId> --json
22
+ ```
23
+
24
+ Read any existing Doing task before selecting another. For each candidate, read
25
+ the task, its goal, and their comments. If the current chat has a Codex Goal,
26
+ confirm that its objective and verification still match these records.
27
+
28
+ ## 2. Check alignment
29
+
30
+ Match the requested outcome to an open PM goal. If none fits:
31
+
32
+ 1. explain the mismatch to the user in Discussion/chat;
33
+ 2. ask whether to refine or create a PM goal, defer the request, or accept a
34
+ narrowly scoped exception;
35
+ 3. record an approved exception as an accepted PM decision and link its id from
36
+ the task;
37
+ 4. do not begin implementation while the choice is unresolved.
38
+
39
+ An agent must not create a convenient goal merely to authorize itself.
40
+
41
+ ## 3. Refine Backlog work and mark it Ready
42
+
43
+ Ready is a durable planner-to-executor handoff. A human project owner or an
44
+ agent holding `pm.plan` may refine the task and mark it Ready. Ordinary managed
45
+ agent handshakes receive `pm.execute`, not `pm.plan`: they may create a Backlog
46
+ proposal or file a bug, but must ask the user/planner to review the contract and
47
+ perform the Ready transition. Do not treat a 403 as a reason to widen or bypass
48
+ the grant.
49
+
50
+ Create/refine the task with one alignment source, an actionable description,
51
+ acceptance criteria, and execution mode:
52
+
53
+ ```cmd
54
+ npx @odla-ai/cli pm task add --app <appId> --title "<work>" --goal <goal-id> --description "<bounded work>" --acceptance "<observable proof>" --execution agent
55
+ npx @odla-ai/cli pm task get <task-id> --json
56
+ npx @odla-ai/cli pm task ready <task-id> --expected-revision <revision>
57
+ ```
58
+
59
+ Use `--alignment-decision <decision-id>` instead of `--goal` only for an
60
+ accepted exception. Ready is stored as `column: "todo"` for wire compatibility.
61
+ The server rejects Ready when the goal/decision belongs to another project, the
62
+ goal is not open, the decision is not accepted, or the contract is incomplete.
63
+
64
+ ## 4. Claim, then create the execution workspace
65
+
66
+ Claim and release require `pm.execute`. Planning authority alone is
67
+ intentionally insufficient, so the principal that approves a task does not
68
+ silently become its executor.
69
+
70
+ Review the latest task revision immediately before claiming:
71
+
72
+ ```cmd
73
+ npx @odla-ai/cli pm task get <task-id> --json
74
+ npx @odla-ai/cli pm task claim <task-id> --expected-revision <revision>
75
+ ```
76
+
77
+ Only one concurrent caller can win. On conflict, reload `pm next` and the task;
78
+ do not retry with a guessed revision. Once claimed, use the repository's
79
+ worktree procedure and mention the PM references in the execution chat:
80
+
81
+ ```text
82
+ @[Task title](pm:task/<task-id>)
83
+ @[Goal title](pm:goal/<goal-id>)
84
+ ```
85
+
86
+ ## 5. Execute, review, and complete
87
+
88
+ - Keep the task description/acceptance criteria current under its revision.
89
+ - Record decisions when the implementation closes off an alternative.
90
+ - File defects as bugs rather than burying them in progress prose.
91
+ - Comment with verification commands, evidence, commit/candidate identity, and
92
+ remaining risk.
93
+ - Move Doing to Review when the acceptance evidence is ready.
94
+ - Mark Done only after review and proof; mark the goal met only when all linked
95
+ work is resolved and the goal proof passes.
96
+
97
+ ## 6. Release or recover
98
+
99
+ If the claimant must stop:
100
+
101
+ ```cmd
102
+ npx @odla-ai/cli pm task release <task-id> --expected-revision <revision>
103
+ ```
104
+
105
+ Comment with the branch/worktree, last verified commit, unfinished step, and
106
+ safe next action. An agent may release only its own claim; a human project
107
+ operator may recover an abandoned claim. Claims do not yet have automatic
108
+ leases, so never leave one active as a substitute for a handoff.
109
+
110
+ ## 7. Resume safely
111
+
112
+ Establish a checkpoint before waiting for PM lifecycle changes, persist every
113
+ checkpoint record, and resume with its opaque cursor:
114
+
115
+ ```cmd
116
+ npx @odla-ai/cli pm watch --app <appId> --entity task --jsonl
117
+ npx @odla-ai/cli pm watch --app <appId> --cursor <cursor> --entity task --state ready --self <principal-id> --jsonl
118
+ npx @odla-ai/cli pm watch --app <appId> --cursor <cursor> --entity task --action comment.created --self <principal-id> --jsonl
119
+ ```
120
+
121
+ Delivery is at least once. Deduplicate by `eventId`; advance durable consumer
122
+ state only on a `checkpoint` record. Filters consume nonmatching events and
123
+ still advance the checkpoint, so do not reuse a filtered consumer cursor for a
124
+ different policy. If the command reports `checkpoint_required` (exit 3), discard
125
+ the stale cursor, run `pm next` and reload any active task, then establish a new
126
+ checkpoint without `--cursor`.
127
+
128
+ A Ready event or PM-attached comment is only a wake-up signal. Before acting,
129
+ re-run `pm next`, reload the task revision, re-check its goal, and win `task
130
+ claim`. General Discussion topics and mentions stay on `discuss watch`; a chat
131
+ message still does not prove a task is Ready or grant execution authority.
@@ -57,7 +57,11 @@ npx @odla-ai/cli pm bug list --app <appId> --status open
57
57
  ```
58
58
 
59
59
  ```cmd
60
- npx @odla-ai/cli pm task set <id> --column doing
60
+ npx @odla-ai/cli pm next --app <appId>
61
+ ```
62
+
63
+ ```cmd
64
+ npx @odla-ai/cli pm task claim <id> --expected-revision <n>
61
65
  ```
62
66
 
63
67
  ```cmd
@@ -66,7 +70,9 @@ npx @odla-ai/cli pm decision list --q "stripe"
66
70
 
67
71
  - `add` needs `--app <appId>` and `--title`; it takes the entity's own fields
68
72
  (`--proof`, `--target` for goals; `--column`, `--goal`, `--assignee`, `--due`
69
- for tasks; `--body` for decisions; `--severity`, `--desc` for bugs).
73
+ for tasks; `--body` for decisions; `--severity`, `--desc` for bugs). A task
74
+ created directly in `todo` (Ready) also needs an open goal or accepted
75
+ alignment decision, a description, and `--acceptance`.
70
76
  - `list` filters with `--status`, `--severity`, `--column`, `--assignee`,
71
77
  `--goal`, full-text `--q`, and pages with `--limit` / `--offset`. **With no
72
78
  `--app` it spans every project you co-own** — that is the cross-project view,
@@ -77,21 +83,49 @@ npx @odla-ai/cli pm decision list --q "stripe"
77
83
  - `comment <id> --body "…"` and `comments <id>` are the item's discussion thread.
78
84
  Status and column changes post there automatically, so the thread is the audit
79
85
  trail — the human sees what you did without asking.
80
- - `rm <id>` deletes. Prefer `done` or a status change; deletion loses the thread.
86
+ - `rm <id>` deletes the current row. Prefer `done` or a status change: the
87
+ authorized immutable event log retains the deletion snapshot for recovery and
88
+ audit until the exact project lifetime is purged.
89
+ - `next --app <appId>` is the read-only intake view: open goals, current Doing
90
+ work, and Ready candidates. It never claims or mutates work.
91
+ - `watch --app <appId> --jsonl` establishes and resumes an exact-project PM
92
+ lifecycle cursor. Persist checkpoint records, deduplicate at-least-once
93
+ delivery by `eventId`, and use `--entity`, `--state`, `--by`, or `--self` for
94
+ one stable consumer policy.
95
+ - `task ready <id> --expected-revision <n>` validates and records the Ready
96
+ contract. `task claim` atomically moves the reviewed Ready revision to Doing;
97
+ `task release` explicitly returns an active claim to Ready. A stale revision
98
+ or concurrent claim is a conflict, so reload instead of overwriting it.
99
+
100
+ Task authority has an intentional handshake boundary. `pm.plan` may refine,
101
+ delete, or mark tasks Ready but cannot claim them. `pm.execute` may create
102
+ Backlog proposals and findings, claim/release, and move its claimed work through
103
+ Review and Done, but cannot approve Ready or change the task contract. Ordinary
104
+ managed-agent approval includes `pm.execute`; it does not include `pm.plan`.
105
+ Human project owners retain planning authority through ownership. Legacy
106
+ `pm.write` grants remain executable so existing claims are not stranded, but do
107
+ not gain planning authority.
81
108
 
82
109
  ## Working with it
83
110
 
84
- **At the start of a session**, read before you write:
111
+ **At the start of a session**, resolve the exact project and read before you
112
+ write:
85
113
 
86
114
  ```cmd
87
- npx @odla-ai/cli pm task list --app <appId> --column doing
115
+ npx @odla-ai/cli context show --context <profile>
116
+ npx @odla-ai/cli pm next --app <appId>
88
117
  ```
89
118
 
90
- then `pm bug list --app <appId> --status open` and
119
+ Then read `pm bug list --app <appId> --status open` and
91
120
  `pm decision list --app <appId> --limit 10`, plus
92
- `pm goal list --app <appId> --status open`. Read the active task, linked goal,
93
- and their comments. That is the state of the project—including anything a
94
- *different* agent left behind.
121
+ the selected task, linked goal, and their comments. That is the state of the
122
+ project—including anything a *different* agent left behind.
123
+
124
+ Before project-mutating work, decide whether the request aligns to an existing
125
+ open goal. If it does not, stop at conversation: explain the mismatch and ask
126
+ the human whether to refine/create a goal or accept a scoped exception as a PM
127
+ decision. Do not silently invent the outcome or begin implementation. Questions,
128
+ investigation, and that alignment conversation do not need a task claim.
95
129
 
96
130
  When coordinating several efforts, begin without `--app` to see doing tasks and
97
131
  open bugs across every co-owned project, then scope each write to the exact
@@ -101,8 +135,18 @@ goal links and comments rather than sharing a working tree or status document.
101
135
 
102
136
  **While you build:**
103
137
 
104
- - Move the card when the work moves `pm task set <id> --column doing` when you
105
- start, `pm task done <id>` when it is really done (tests green, not "written").
138
+ - Propose work in Backlog. A human owner or `pm.plan` agent reviews/refines it,
139
+ links it to an open same-project goal (or accepted alignment decision), adds
140
+ an actionable description and acceptance criteria, then runs `pm task ready
141
+ <id> --expected-revision <n>`. The stored column is still `todo` for API
142
+ compatibility, but every product surface calls it Ready.
143
+ - An agent starts only with `pm task claim <id> --expected-revision <n>`. The
144
+ compare-and-set claim assigns the authenticated principal and moves the task
145
+ to Doing. Never use `pm task set --column doing` to bypass it.
146
+ - Use one claimed task and one owned worktree/branch. If you must stop before
147
+ review, run `pm task release <id> --expected-revision <n>` and comment with
148
+ the recovery point. Move to Review when evidence is ready; mark Done only
149
+ after the acceptance criteria pass.
106
150
  - Record a decision the moment you make it, with what it rules out:
107
151
  `pm decision add --app <appId> --title "Bun over tsx for the worker build"
108
152
  --body "…"`. This is the single highest-value thing you can do here.
@@ -113,6 +157,90 @@ goal links and comments rather than sharing a working tree or status document.
113
157
  `pm goal add --app <appId> --title "Checkout survives a retry storm" --proof
114
158
  "test/checkout-retry.test.ts"`. Mark it met only when that proof passes.
115
159
 
160
+ ## Referencing PM records in Discussion
161
+
162
+ Discussion markup uses stable typed references. Prefer it over pasting a title
163
+ that can be renamed:
164
+
165
+ ```cmd
166
+ npx @odla-ai/cli pm task ref <task-id>
167
+ ```
168
+
169
+ Paste the returned markup into `--markup`, alongside other references:
170
+
171
+ ```cmd
172
+ npx @odla-ai/cli discuss post --app <appId> --subject "Ready for review" --markup "Review @[Checkout retries](pm:task/<task-id>) for @[Retry safety](pm:goal/<goal-id>)."
173
+ ```
174
+
175
+ The same copied `pm:*` markup works in an item's own PM comment. It is stored as
176
+ a structured reference, rendered as a PM chip, and printed as markup again by
177
+ the `comments` command:
178
+
179
+ ```cmd
180
+ npx @odla-ai/cli pm task comment <task-id> --body "Blocked by @[Retry safety](pm:goal/<goal-id>)."
181
+ npx @odla-ai/cli pm task comments <task-id>
182
+ ```
183
+
184
+ Use `pm:task`, `pm:goal`, `pm:decision`, `pm:bug`, and `pm:runbook`. The message
185
+ is communication; the referenced PM row remains the lifecycle authority.
186
+
187
+ In Studio, open any PM item and choose **Discuss** to open the owning project's
188
+ Discussion composer with the same stable reference already inserted. The draft
189
+ is retained in the URL across reload/back navigation but is not a write: review,
190
+ edit, and explicitly post it, or cancel to discard the route draft. This bridge
191
+ does not create a canonical one-topic-per-item stream; PM-attached comments and
192
+ general project Discussion still have independent capability checks and watches.
193
+
194
+ ## Credentials and worktrees
195
+
196
+ A repository-local `.odla/dev-token.json` is not shared by linked worktrees.
197
+ For work that must run from several worktrees or another project, save a named,
198
+ non-secret operator context and select it explicitly:
199
+
200
+ ```cmd
201
+ npx @odla-ai/cli context save delivery --platform https://odla.ai --app <appId>
202
+ npx @odla-ai/cli context show --context delivery
203
+ npx @odla-ai/cli pm next --context delivery
204
+ ```
205
+
206
+ The selected profile uses a user-level private credential cache instead of the
207
+ checkout-local cache. Credentials still expire. During the next handshake,
208
+ reusing the same immutable, human-approved project handle reconnects the new
209
+ credential to the existing agent principal. Follow
210
+ [`agent-identity.md`](./agent-identity.md): collection atomically retires the
211
+ older collected ordinary-handshake credential and replaces the principal's
212
+ manager-issued grants with the newly reviewed exact-project set. Approval alone
213
+ does neither, so a failed collection leaves current access intact. Deliberate
214
+ manual/scoped credentials still require explicit inventory review.
215
+
216
+ ## State-change delivery
217
+
218
+ Every goal/task/decision/bug create/update/delete appends an immutable event
219
+ snapshot in the same transaction. Consume the exact-project stream with:
220
+
221
+ ```cmd
222
+ npx @odla-ai/cli pm watch --app <appId> --jsonl
223
+ npx @odla-ai/cli pm watch --app <appId> --cursor <cursor> --entity task --state ready --self <principal-id> --jsonl
224
+ npx @odla-ai/cli pm watch --app <appId> --cursor <cursor> --entity task --action comment.created --self <principal-id> --jsonl
225
+ ```
226
+
227
+ The first call establishes a checkpoint and intentionally does not replay old
228
+ activity. Persist `checkpoint` records; delivery is at least once, so deduplicate
229
+ by stable `eventId`. A `checkpoint_required` status means retention/restore made
230
+ the cursor stale: re-read `pm next` and the active task, then establish a fresh
231
+ checkpoint. Filters consume nonmatching events, so a saved cursor belongs to one
232
+ filter policy.
233
+
234
+ `pm watch` covers PM lifecycle rows plus comments attached to active goals,
235
+ tasks, decisions, and bugs. It deliberately does not copy general project chat:
236
+ `discuss watch` still carries ordinary topics and mentions under the independent
237
+ Discussion capability. They are not yet one canonical topic/stream. Never infer
238
+ authority to start from either notification alone: re-read the Ready row and win
239
+ the atomic claim at its current revision.
240
+
241
+ The focused start/claim/recovery procedure is also available in
242
+ [`pm-work-intake.md`](./pm-work-intake.md).
243
+
116
244
  **Never** put a secret, a token, or a credential in a title, body, description,
117
245
  or comment. PM items are shared with every co-owner of the app.
118
246
 
@@ -79,12 +79,13 @@ re-run `calendar connect`.
79
79
  ## @odla-ai/ai — inference (Claude / GPT / Gemini)
80
80
 
81
81
  ```ts
82
- const { ai } = await initFromPlatform({ platform, appId, env, db });
83
- await ai.chat({ messages }); // provider/model + key resolved from the platform vault
82
+ const { ai } = await initFromPlatform({ platform, appId, env, appKey, db });
83
+ await ai.chat({ messages, maxTokens: 512 }); // approved hosted model; db supports BYOK fallback
84
84
  ```
85
85
 
86
- No API key in your code or env it lives in the tenant vault; `provision`
87
- stores it when the configured `ai.keyEnv` is set at provision time.
86
+ No provider key in your code or env. Hosted AI uses the app's existing backend
87
+ key and central cost attribution; `--ai-provider` explicitly opts a new config
88
+ into BYOK.
88
89
 
89
90
  ## @odla-ai/crm — records, pipelines, follow-ups, and contactability
90
91
 
@@ -51,7 +51,7 @@ Resolve `appId` from `odla.config.mjs`; do not guess it from a repository name,
51
51
  deployment, old branch, or conversation.
52
52
 
53
53
  ```cmd
54
- npx @odla-ai/cli pm task list --app <appId> --column doing
54
+ npx @odla-ai/cli pm next --app <appId>
55
55
  npx @odla-ai/cli pm bug list --app <appId> --status open
56
56
  npx @odla-ai/cli pm decision list --app <appId> --limit 20
57
57
  npx @odla-ai/cli pm goal list --app <appId> --status open
@@ -63,9 +63,15 @@ being tested. If PM and the working tree disagree, stop, investigate, and
63
63
  record the resolution as a decision or comment; do not silently pick whichever
64
64
  is more convenient.
65
65
 
66
+ If the requested migration work does not align to an open goal, discuss it with
67
+ the human before implementing. Refine the phase task with a concrete
68
+ description and acceptance criteria, mark the reviewed revision Ready, and use
69
+ the atomic claim operation before creating its worktree. On interruption,
70
+ release the claim with the latest revision and comment with the recovery point.
71
+
66
72
  ## Update at every gate
67
73
 
68
- - Move the phase/track task when work starts.
74
+ - Mark the refined phase/track Ready, then atomically claim it when work starts.
69
75
  - Comment with the exact verification command, result, source commit, candidate
70
76
  deployment version, and rollback checkpoint.
71
77
  - Record an intentional contract or product delta as a decision.