Skip to content

Commit ac35e2a

Browse files
authored
docs(adr): record the ADR-0006 API-surface boundary — three surfaces keep project deliberately (#12473) (#12736)
1 parent 2082180 commit ac35e2a

1 file changed

Lines changed: 130 additions & 1 deletion

File tree

‎docs/adr/0006-project-environment-split.v4.md‎

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0006: Environment & Project — v4 (drop dev-workspace Project, unify on Package)
22

3-
**Status**: Accepted (v4 — supersedes v3)
3+
**Status**: Accepted (v4 — supersedes v3) — API-surface vocabulary boundary recorded 2026-08-27 (#12473): the v5.0 `project` → `environment` rename stops at the CLI's user-facing vocabulary; three API surfaces keep `project` deliberately (see the addendum)
44
**Date**: 2026-05-20 (v4)
55
**Deciders**: ObjectStack Protocol Architects
66
**Supersedes**: v1 (strict tree), v2 (siblings + sys_deployment join), v3 (siblings + deferred dev-workspace `sys_project`)
@@ -252,3 +252,132 @@ when needed.
252252
- Power Platform: Solution → Environment model (same shape, different names)
253253
- Salesforce: Unlocked Package → Org model (no "Project" table either)
254254
- npm + lockfile: identity + immutable version + installation pointer
255+
256+
---
257+
258+
## Addendum (2026-08-27, #12473) — the rename stops at the CLI's user-facing vocabulary: three API surfaces keep `project` deliberately
259+
260+
**Provenance.** Maintainer ruling on
261+
[#12473](https://github.com/objectstack-ai/objectstack/issues/12473), 2026-08-27,
262+
adjudicated in the PM decision-inbox batch, accepting that card's option 3 —
263+
verbatim, untranslated: 「其他同意」. The ruling names this addendum as the
264+
deliverable and names nothing else: no code change rides it, because on this
265+
question **not touching the code is the decision**.
266+
267+
**Why the boundary is written here rather than left implicit.** The v5.0 breaking
268+
rename `project` → `environment` — AGENTS.md, top of file: *"No aliases. See
269+
ADR-0006. 'Project' now only means the npm/monorepo sense."* — was carried
270+
through the CLI in three passes: the command name (#10967), the entity noun in
271+
every string `os environments` prints (#12153), and the comment axis (#12432).
272+
Each pass correctly fenced the API surface out of its own scope, and none could
273+
close the question, so the same seam produced a fourth card (#12464) and then a
274+
decision card, inside one week. An unwritten boundary is re-litigated by whoever
275+
next reads the two spellings side by side — human or agent — and the cost of
276+
option 3 is exactly this section. If you arrived here from such a sighting: it is
277+
deliberate, it is below, and the way to change it is D2, not a PR.
278+
279+
### D1 — Three surfaces retain `project` deliberately
280+
281+
Identified by **quoted phrase, not line number**. The card that ordered this
282+
addendum anchored one of the three to a line number, and that anchor was already
283+
stale when the addendum was written; the phrases below were measured on `main`
284+
on 2026-08-27.
285+
286+
1. **The SDK method namespace** — the `projects` block on the `@objectstack/client`
287+
client class (`packages/client/src/index.ts`), reached by callers as
288+
`client.projects.list`, `client.projects.get`, `client.projects.create`,
289+
`client.projects.update`, `client.projects.delete`, `client.projects.activate`
290+
and the remaining methods of that block, including the environment-scoped
291+
`projects.packages` methods nested inside it. Note what is *already* renamed
292+
underneath it: every method in the block calls `/api/v1/cloud/environments`.
293+
The namespace identifier is the only `project` spelling left on that face —
294+
and it is a **published** method name, so renaming it breaks every caller,
295+
while an alias is precisely what this rename does not get.
296+
297+
2. **The control-plane response fields** — the response envelope keys `project`
298+
and `projects` that the `/api/v1/cloud/environments` endpoints return. What
299+
this repository can measure is the **consumer** side, and this addendum claims
300+
nothing beyond it: the SDK declares the unwrapped shapes (`{ projects: any[];
301+
total: number }` on list, `{ project: any; database: any }` on create,
302+
`{ project: any }` on update), and the CLI reads `res?.projects ?? []` in
303+
`packages/cli/src/commands/environments/list.ts` and `res?.project` in the
304+
sibling `create.ts` and `show.ts`. The **producer** is the cloud control plane
305+
in `objectstack-ai/cloud`, which is not this repository: these fields cannot be
306+
renamed from here at all, which is why the rename of this surface is a
307+
cross-repo coordination, not a local edit.
308+
309+
3. **The SDK JSDoc that travels with them** — the sentence *"Provision a new
310+
project. Delegates to `ProjectProvisioningService.provisionProject` on the
311+
server."*, on the `create` method of that same namespace. It is retained by the
312+
same ruling and for a reason of its own: it names a server-side class that is
313+
part of the contract in point 2, so renaming the sentence alone would make the
314+
comment describe the running system **less** accurately, not more. Editing it
315+
is executing option 2 below, which is permanently declined.
316+
317+
⇒ A PR that "tidies" any of these three is not a cleanup. Under Prime Directive
318+
13 it reverses a recorded decision, and the route for that is D2 — not a
319+
changeset, and not a drive-by rename.
320+
321+
### D2 — Option 1 (full rename) is pre-registered: **deferred, not abandoned**
322+
323+
Option 1 is to rename **both** halves together — the SDK method namespace to
324+
`environments` and the wire fields to `environment` / `environments` — with no
325+
aliases, as a breaking major coordinated with `objectstack-ai/cloud`, carrying a
326+
migration entry. That is the option consistent with the no-alias rename, and it
327+
remains the direction of travel: the target-state vocabulary has one word for
328+
this concept, and it is `environment`.
329+
330+
It is **pre-registered to reopen at the next planned SDK/protocol breaking
331+
major.** What defers it is not that the drift is acceptable forever, but that
332+
today it would buy a zero-pull cross-repo breaking change: no user is blocked by
333+
the `project` spelling, and the only measured cost of the drift so far is
334+
internal — the card stream this addendum exists to stop. At the next such major
335+
the coordination is already being paid for, and the rename rides with it.
336+
337+
To whoever plans that major: this entry is your trigger. Reopen #12473's option
338+
1, coordinate the producer change with the cloud repository, and retire this
339+
addendum's D1 in the same release rather than adding an alias to soften it. The
340+
standing startup-stage retirement rule does **not** force this earlier — it
341+
governs deprecated-alias retirements, not a zero-pull cross-repo major.
342+
343+
### D3 — Option 2 (SDK-only half-rename) is **permanently declined**, and this is why
344+
345+
Option 2 is to rename the SDK namespace to `environments` while the wire fields
346+
stay `project` / `projects`. It is the cheapest-looking move on the board and it
347+
is refused permanently, for three reasons that do not expire with the price:
348+
349+
- **The two halves of one contract would say different things.** The SDK would
350+
teach `environment`; the payload it hands back would say `project`. A contract
351+
that contradicts itself across a single call is worse than one that is
352+
uniformly out of date, because the reader cannot tell which half is the
353+
mistake.
354+
- **The mapping layer becomes permanent.** The CLI's translation between the two
355+
spellings exists today as an artifact of an unfinished rename, and it is
356+
cheap only while it is understood as temporary. Half-renaming promotes it to
357+
a load-bearing, permanently-maintained seam that every future consumer must
358+
reimplement.
359+
- **It is the worst shape for AI authors** — the axis this project weighs
360+
explicitly. An agent that reads the SDK learns one vocabulary, an agent that
361+
reads a response payload learns another, and every generated call site has to
362+
infer which side of the mapping it is standing on. Uniform-but-old is
363+
learnable; split-brain is a guess per call site, and a wrong guess typechecks.
364+
365+
A prohibition without a reason gets stepped over by the next author who finds it
366+
inconvenient. The reason is above; if it is ever wrong, the reversal is a new
367+
ruling, not a rename PR.
368+
369+
### What this addendum does not change
370+
371+
No code, no schema, no route, no changeset. The three surfaces in D1 are correct
372+
as they stand today, and the correct action on them is **none**. The CLI's
373+
user-facing vocabulary stays fully renamed — `os environments`, its printed
374+
nouns, and its comments are all `environment`, and nothing here reopens them.
375+
Nothing here touches the `objectstack-ai/cloud` producer either.
376+
377+
### Why an addendum, not a new ADR
378+
379+
The question is this record's own: where the boundary between the two senses of
380+
"project" sits, and which surfaces the v5.0 rename reaches. A separate ADR would
381+
put the boundary in one file and the rename it bounds in another, so the reader
382+
who follows AGENTS.md's *"See ADR-0006"* would arrive at the record that does not
383+
carry the answer — the exact failure this addendum was ordered to fix.

0 commit comments

Comments
 (0)