From 9694cf67d7b35c6bee02975c7e380281c47bd425 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <50353452+hotlong@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:16:11 +0800 Subject: [PATCH] =?UTF-8?q?docs(adr):=20retire=20ADR-0006=20D1=20by=20exec?= =?UTF-8?q?ution=20=E2=80=94=20the=20three=20retained-`project`=20surfaces?= =?UTF-8?q?=20are=20renamed=20(#12867)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D2 was pre-registered to reopen at the next SDK/protocol breaking major. The maintainer's 2026-08-28 ruling on the epic card opened that window, so D2 fired and D1's three deliberately-retained surfaces are renamed with no aliases. Adds a second dated addendum recording the retirement, extends the Status-line pointer, and marks D1 in-body as RETIRED while leaving its text intact so the record of why the surfaces were retained stays legible. --- docs/adr/0006-project-environment-split.v4.md | 132 +++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/docs/adr/0006-project-environment-split.v4.md b/docs/adr/0006-project-environment-split.v4.md index 9c73ec9a21..7ba992b73e 100644 --- a/docs/adr/0006-project-environment-split.v4.md +++ b/docs/adr/0006-project-environment-split.v4.md @@ -1,6 +1,6 @@ # ADR-0006: Environment & Project — v4 (drop dev-workspace Project, unify on Package) -**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) +**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). **D1 retired by execution (2026-08-28, #12867 — see the second addendum):** D2 fired on the maintainer's 2026-08-28 ruling; all three of those surfaces are renamed to `environment` / `environments` with no aliases, authored and parked for one coordinated release. Read the first addendum's D1 as history — the boundary it draws no longer exists. **Date**: 2026-05-20 (v4) **Deciders**: ObjectStack Protocol Architects **Supersedes**: v1 (strict tree), v2 (siblings + sys_deployment join), v3 (siblings + deferred dev-workspace `sys_project`) @@ -278,6 +278,13 @@ deliberate, it is below, and the way to change it is D2, not a PR. ### D1 — Three surfaces retain `project` deliberately +> **RETIRED 2026-08-28 (#12867, by execution of D2 — see the second addendum at +> the end of this file).** All three surfaces below are renamed; none of them +> retains `project` any more. This section is kept unedited because it is the +> record of *why* they were retained through 2026-08-27 and of what the +> retention cost, and because D2 was drafted against these exact phrases. Read +> it in the past tense. + Identified by **quoted phrase, not line number**. The card that ordered this addendum anchored one of the three to a line number, and that anchor was already stale when the addendum was written; the phrases below were measured on `main` @@ -381,3 +388,126 @@ The question is this record's own: where the boundary between the two senses of put the boundary in one file and the rename it bounds in another, so the reader who follows AGENTS.md's *"See ADR-0006"* would arrive at the record that does not carry the answer — the exact failure this addendum was ordered to fix. + +--- + +## Addendum (2026-08-28, #12867) — D2 fired: D1 is retired by execution, and all three surfaces now say `environment` + +**Provenance.** Maintainer ruling in live PM chat, 2026-08-28, recorded verbatim +and untranslated on the epic card +[#12865](https://github.com/objectstack-ai/objectstack/issues/12865): 「作为 epic +卡,处理所有相关任务和开发」. That instruction satisfies the restart clause D2 was +pre-registered under — D2 declares itself *"pre-registered to reopen at the next +planned SDK/protocol breaking major"* — and opens the window. D2 is therefore no +longer deferred: it executed. + +**What this section is.** D2's closing instruction to whoever planned that major +was to *"retire this addendum's D1 in the same release rather than adding an +alias to soften it."* This is that retirement, and it is the record half only — +the code half is the two pull requests named under the landing constraint below. + +### The three D1 surfaces, retired one by one + +Cited by **quoted phrase, not line number**, on the discipline D1 set for itself +after its own predecessor's line-number anchor went stale. Each quotation below +is D1's; each "now" is what the executing PRs author. + +1. **The SDK method namespace.** D1 retained *"the `projects` block on the + `@objectstack/client` client class"*, reached as `client.projects.list`, + `client.projects.get` and the rest of that block, *"including the + environment-scoped `projects.packages` methods nested inside it"*. **Now:** + the block is `environments`; every method moves with it, and the nested + package methods are reached as `client.environments.packages.*`. **No + alias** — no deprecated forwarder, no compatibility getter, nothing. That is + the standing rule D1 itself quoted from AGENTS.md (*"No aliases. See + ADR-0006."*), and softening the retirement with an alias would have removed + the boundary while keeping the drift it existed to fence. + +2. **The control-plane response fields.** D1 retained *"the response envelope + keys `project` and `projects` that the `/api/v1/cloud/environments` + endpoints return"*, noting carefully that the producer *"is the cloud control + plane in `objectstack-ai/cloud`, which is not this repository"*. **Now:** the + producer half renames them at **eight emission sites** — five in + `packages/service-cloud/src/routes/environment-crud.ts`, three in + `packages/service-cloud/src/routes/environment-lifecycle.ts` — so every + payload key becomes `environment` / `environments`, with no dual-key emission + and no compatibility window. The consumer side in this repository follows in + the same release: the SDK's declared unwrap shapes, and the CLI readers D1 + named (`res?.projects ?? []` in + `packages/cli/src/commands/environments/list.ts`, `res?.project` in the + sibling `create.ts` and `show.ts`). + + **A correction D1 could not have made, surfaced by executing it.** D1 quoted + the SDK's declared create shape as *"`{ project: any; database: any }` on + create"*, and said plainly that the consumer side was all this repository + could measure. Measured against the control plane while executing the + rename, that declaration turned out not to be merely pre-rename but + **false**: the route `create` actually reaches has never emitted a `project` + key, and emits no `database` key either — callers were typed to expect a + field the server never sends, so reading it was a runtime error the types + promised could not happen. The rename therefore corrects the shape to + `{ environment: any }` instead of transliterating it. This is the standing + cost of a contract whose two halves live in repositories that never compile + against each other, and it is the sharpest argument on record for why the + halves must land together. + +3. **The SDK JSDoc that travels with them.** D1 retained the sentence + *"Provision a new project. Delegates to + `ProjectProvisioningService.provisionProject` on the server."*, reasoning + that *"it names a server-side class that is part of the contract in point 2, + so renaming the sentence alone would make the comment describe the running + system less accurately, not more."* **Now:** the sentence names the endpoint + — *"Provision a new environment — `POST /api/v1/cloud/environments`."* — + because the premise of that reasoning did not survive measurement. The named + class has **zero hits** in the cloud repository's `packages/service-cloud` + (measured 2026-08-28 while executing the rename; a repo-wide grep at this + addendum's writing found none either). It was renamed or removed there and + this docblock rotted in silence, because no gate in this repository compiles + against that implementation. The endpoint is the one identifier the method + itself constructs, so it is the only one an in-repo reader can verify. D1's + *intent* — the comment must describe the running system truthfully — is what + is preserved here; its chosen instrument was pointing at something that was + no longer there. + +### D3 is untouched, and the pairing is why + +Option 2, the SDK-only half-rename, remains **permanently declined** on the +three reasons D3 gives, none of which this execution weakens. What executed is +Option 1: both halves, together, in one release. If either pull request below +lands without the other, the result is D3 **by accident** — the exact shape D3 +refuses — which is why the pairing is a landing constraint and not a +preference. + +### Landing constraint — one coordinated release + +This addendum records **authored and parked** work, not released work. Both +halves exist as open **draft** pull requests held for the maintainer's +coordinated window: + +- SDK half (#12866) — [PR #12885](https://github.com/objectstack-ai/objectstack/pull/12885): the namespace, the unwrap shapes, the JSDoc, and the CLI consumers. +- Producer half (`objectstack-ai/cloud`#1691) — [cloud PR #1692](https://github.com/objectstack-ai/cloud/pull/1692): the eight response-key emission sites. + +This addendum rides that same window. Merging it while either rename PR is still +parked would leave the record claiming a retirement the code has not performed — +the failure mode an ADR is worst at recovering from, because the next reader +trusts the record over the code. Governed surface: draft only, human merge, +never auto-merge. + +### What this retirement does not change + +The CLI's user-facing vocabulary, already fully renamed and explicitly left +alone by the first addendum. The v4 body above — the Environment / Package +model, the phasing, the migration — is untouched: this retires a vocabulary +boundary, it does not change the model. D3's prohibition stands. One +`project`-spelled SDK surface outside D1's three is being adjudicated separately +on [#12882](https://github.com/objectstack-ai/objectstack/issues/12882), and +this addendum takes no position on it. + +### Why this rides ADR-0006 rather than a new record + +The same reason the first addendum gave, now applied to its own retirement: a +reader who follows AGENTS.md's *"See ADR-0006"* has to land on the record that +carries the **current** answer. Filing the retirement as a separate ADR would +recreate precisely the split the first addendum was ordered to fix — the +boundary in one record and the fact that the boundary is gone in another — and +the reader who found only the first would act on a rule that no longer holds.