Skip to content

docs(skills,adr): teach the code-defined datasource credential route (OS_DATASOURCE_NAME_PASSWORD behind the host switch) - #28432

Merged
os-zhuang merged 1 commit into
mainfrom
claude/project-thread-c92hml
Oct 31, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/project-thread-c92hml

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #28209

Clause-②: no — governed prose (two ADR amendment notes) and one published skills rules file. No schema, export, type, key, value, error code or runtime behaviour moves; nothing is newly accepted or refused.
Metadata-protocol: none

维护者速读

What changes

Measured on origin/main 5da3a8ce before any edit

What PR #28208 shipped, with PR #28325's two corrections, cited by file and line on that tree:

Fact Where
Variable name rule: OS_DATASOURCE_ + the datasource name upper-cased with every character outside [A-Z0-9] replaced by _ + _PASSWORD packages/services/service-datasource/src/datasource-env-credential.ts:55-57 (datasourceEnvCredentialVar)
Which names never map: default (an asDefault connect never reads the slot) packages/services/service-datasource/src/datasource-connection-service.ts:772
Which names never map: a name outside the host's code-datasource set (a Setup- or API-created datasource), or a connect whose trigger is not declared-auto datasource-connection-service.ts:936-941 (envCredentialVarFor)
No two schema-valid names derive one variable: the name regex is lower-case ^[a-z_][a-z0-9_]*$, on which upper-casing is injective packages/spec/src/data/datasource.zod.ts:652
The host switch name, OS_DATASOURCE_ENV_CREDENTIALS datasource-env-credential.ts:44
Its reading: unset or empty is off; on / off in any letter case; any other value throws datasource-env-credential.ts:65-78 (resolveDatasourceEnvCredentialsSwitch)
The throw refuses the boot: the admin plugin reads the switch inside init() and wires the slot only when it is on packages/services/service-datasource/src/datasource-admin-plugin.ts:338 (init), :421-429 (the envCredentials spread)
os serve stamps on when the operator left it unset or empty, just before it builds the admin plugin packages/cli/src/commands/serve.ts:5287-5289; packages/cli/src/utils/datasource-env-credentials-default.ts:20-28
os start and os dev run serve, so they get the stamp packages/cli/src/commands/start.ts:528, packages/cli/src/commands/dev.ts:848
Library compositions: nothing else calls the stamp, and the service's envCredentials is optional and absent by default datasource-connection-service.ts:171; datasource-env-credential.ts:80-89
Resolution order inside connect(): credentialsRef (fail-closed) → the D7 connector borrow (fail-closed) → the env slot, only when neither is declared and not asDefault → the driver factory datasource-connection-service.ts:713-738, :749-757, :770-795, :805 (factory.create)
The value becomes the build's secret; an empty or unset variable leaves envCredentialVar set so the failure can name it datasource-connection-service.ts:791-793
Collision refusal: a second live name deriving the same variable is refused failed-credentials, naming both datasources and the variable, before any read datasource-connection-service.ts:775-786
A failed connect releases its claim on the variable (PR #28325) datasource-connection-service.ts:911
Failure text as PR #28325 left it: the driver's error, then (note: datasource 'NAME' is defined in code and reads its credential from OS_DATASOURCE_NAME_PASSWORD at connect, which is not set on this host); verdict failed-degraded datasource-connection-service.ts:921-924
The code-datasource set's three producers: the boot artifact, default, and a package installed into the running kernel (the trust consequence) packages/runtime/src/app-plugin.ts:626, packages/runtime/src/default-datasource-plugin.ts:150, packages/runtime/src/declared-datasources.ts:188
The spec's shared sentence naming the route, appended to five credential refusals, the refused-key description and the two password guidance entries packages/spec/src/data/driver/common.zod.ts:103-108 (CODE_DEFINED_CREDENTIAL_ROUTE); 7 uses there, 2 in packages/spec/src/data/datasource.zod.ts

The three texts

skills/objectstack-data/rules/datasources.md (reader: the customer's agent writing a defineDatasource or a package's *.datasource.ts; mistake prevented: inlining a password, or writing a credentialsRef handle nothing mints for a code datasource, or expecting a library-built kernel to read the variable).

  • The section is now "Credentials — fail-closed". Its opening paragraph says: a code-declared datasource reads its password at connect from OS_DATASOURCE_NAME_PASSWORD (NAME = the name upper-cased; exact for every schema-valid name, since the only non-alphanumeric character a valid name can hold is _); only while the host switch OS_DATASOURCE_ENV_CREDENTIALS is on; os serve / os start / os dev default it on, a library composition is off, and the switch takes on / off only; the operator's caution that an installed package may declare a datasource of any free name and so read that variable; and external may instead name one channel, which wins and never falls back to the variable. The credentialsRef and connector bullets and the fail-closed paragraph are kept.
  • Paid under the ratchet by deleting restatements in the same file, each with a home elsewhere in the bundle:
    • the "Full field reference" pointer to datasource.zod.ts — skills/objectstack-data/references/_index.md:12 carries the same path;
    • the // writable: true comment in the example — the Writes section two screens down states the gate;
    • the "✅ / ❌ Column mapping" heading and its ✅ bullet — the example's // columnMap: { cust_region: 'region' }, // remoteColumn → localField line shows the same key with its direction; the ❌ field.columnName rule stays;
    • "(default: 'default')" on the object field — skills/objectstack-data/SKILL.md:87's field table carries that default;
    • "and its federated objects registered," in the auto-connect sentence, "(at defineStack, else at connect)" on the connector refusal, and "Otherwise every write to the object is rejected." after "read-only by default … both gates must be on" — consequence and timing clauses whose rule survives in the sentence they hung on.
  • No re-wrap bought a line: every line that moved either lost a clause or is new text.
  • Two readings, lines and tokens (the ratchet's unit): the file 84 → 83 lines, 909 → 910 tokens against a ceiling of 911 (headroom 2 → 1); the whole shipped bundle 219807 → 219808 tokens; the sum of every SKILL.md 56506 → 56506 (none edited). No ceiling moves.
  • Not taught, on purpose: the collision refusal (no author can write two valid names that collide), the unset-variable failure text (the reason names the variable at runtime), and the per-request plugin-driver limit (no reader among the agents this file serves). All three are recorded in the ADR-0015 note.

docs/adr/0015-external-datasource-federation.md

docs/adr/0062-external-datasource-runtime.md

  • A dated amendment blockquote under D3, the runtime decision: the slot is a second resolution source at the same point, after credentialsRef and the D7 borrow and before the factory, with the five conditions under which it is taken, the switch and its wiring at init(), the failure text, the collision claim, and a pointer to ADR-0015 §10 for the operator's caution. The normative bullets are not edited, as the ADR's other amendments did not edit theirs.

The changeset

.changeset/28209-skills-code-datasource-env-credential.md: @objectstack/skills patch, carrying both declaration lines. skills/** is copied into that package's dist/skills by its build and ships in its files[], so this diff publishes; see Acceptance notes for the conflict with the order.

Tests

No code changes; the gates below are the tests. The published surface was built: pnpm --filter @objectstack/skills build under the verify lock → sync-catalog self-test: 9 cases pass, 95 file(s) across 11 skill(s) copied from skills/** into dist/skills and read back byte-identical, VERDICT command-exit 0.

Acceptance notes

  • The order ruled skip-changeset; the repo's rule says otherwise, and this PR follows the repo. AGENTS.md's Post-Task Checklist: skip-changeset "is for a diff that publishes nothing from any released package". scripts/check-empty-changeset.mjs (its failure text and its GREEN 3 battery) states that a skills/** change ships in @objectstack/skills and "owes a changeset naming that package, never the skip-changeset label", and the precedent changesets for skills-only PRs (for example .changeset/26475-skills-teach-queues-assignment-rules.md) are @objectstack/skills: patch. .claude/agents/os-dev.md says that where an order and that file conflict, the file wins and the conflict is named, so: a patch changeset, no skip-changeset, and zero label writes at pr_create (the order named no other label). The seat relabels if the maintainer rules otherwise.
  • Security. No credential value, connection string or example password anywhere in the diff or this body; variable names only. The trust note is stated as an operator's caution in each text, with no recipe.
  • Out of scope, reported, not edited: skills/objectstack-data/evals/hooks-security-seeds-datasources.json, case id 3, still answers "A defineDatasource entry (credentialsRef, never inline credentials)" for a code datasource exposing a Postgres table — the same stale teaching this card corrects, in a fixture the card and the claim's file surface do not name. Its assertions do not require credentialsRef, so it stays green. Reported in the os-dev-report for the seat to file or fold in.
  • content/docs/data-modeling/external-datasources.mdx §4 (PR feat(service-datasource): a code-defined datasource reads its credential from OS_DATASOURCE_<NAME>_PASSWORD #28208) already teaches the full route; the skills text is deliberately the short form and names no #NNNN.
  • pnpm lint scoped to touched packages: the diff touches no package source; @objectstack/skills has no lint script and eslint reads no .md, so there is nothing to scope it to. Recorded, not run.

Verification

All on head 17988ade (base origin/main 5da3a8ce; no merge, main did not move under the branch).

  • node scripts/pm/check-governed-merges.mjs --test skills/objectstack-data/rules/datasources.md docs/adr/0015-external-datasource-federation.md docs/adr/0062-external-datasource-runtime.md → exit 3, governed-surface predicate: 3 of 3 path(s) hit the register (13 surfaces govern objectstack). ⛔ GOVERNED — a human merge is the review record for this PR … landing tier: H(人合): docs/adr/** ×2, skills/** ×1. The PR stays draft; no seat readies, queues or arms it.
  • node scripts/check-skills-token-ratchet.mjs → exit 0: skills/objectstack-data/rules/datasources.md is 910 tokens (ceiling 911; headroom 1); 56 authored bundle file(s) within their ceilings; bundle total 219807 → 219808.
  • pnpm check:pm-skill-id-lint → exit 0: 105 file(s) clean (pattern /#[0-9]{3,}/g). pnpm check:doc-authoring → exit 0: 70 published skill files clean — no internal issue-id references.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on this diff: 49 families. Every one was run with its exit code captured before any pipe (timeout 900 bash -c CMD; EXIT=$?), and --ran reconciles to 49 derived famil(ies) accounted for — 47 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3). Three first exited 3 (PREREQUISITE NOT MET, nothing measured). pnpm --filter @objectstack/lint run check:doc-formula-expressions wanted @objectstack/formula and then @objectstack/lint built: after pnpm --filter '@objectstack/formula...' build and pnpm --filter '@objectstack/lint...' build under the verify lock (VERDICT command-exit 0, 88s and 81s) it exits 0 — 9 @example(s) judged clean across 2321 packages/spec/src files, 15 predicate(s) … judged clean. The two left to CI's full build: pnpm check:dual-build-cjs-loads (needs pnpm build) and pnpm check:lean-entry-closure (needs the built entry points, packages/objectql/dist/core.mjs among them); neither reads a file in this diff. Among the 46 green: check:adr-anchors, check:adr-index, check:adr-symbol-anchors, check-adr-links (962 relative links resolve), check-adr-0087-registration, check-changeset-no-major, check-empty-changeset (1 declaring changeset added, none from the merge base modified), check:pm-governed-merges, check:pm-changeset-deadline-census, check:pm-prior-rulings, check:published-files, check:skill-compatibility, check:skill-frame-sync, check:skill-identifier-liveness, check:dts-closure, check:nul-bytes (23088 text files, no raw control bytes). The three diff-reading gates that ran before the commit (check-adr-0087-registration --base, check-adr-links, check-changeset-no-major --base) were re-run on the committed head: all exit 0.
  • pnpm --filter @objectstack/skills build under scripts/pm/os-verify-lock.sh → sync-catalog self-test: 9 cases pass; 95 file(s) across 11 skill(s) copied from skills/** into dist/skills and read back byte-identical; VERDICT command-exit 0 · held the lock 2s.
  • pnpm lint scoped: nothing to scope to (no package source in the diff; @objectstack/skills has no lint script).
  • Ablation: none — no tested behaviour changes (docs and skills only).
  • Dogfood battery: n/a — no ! in the title and no (narrowing) arm.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (packages/spec untouched.)
  • 2. Every name I added uses the casing for its kind. (No new names; the variable names are the shipped ones.)
  • 3. No formatter ran; pnpm lint passes. (No package source touched; see Acceptance notes.)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is the required Chinese summary).
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

…(OS_DATASOURCE_NAME_PASSWORD behind the host switch)

skills/objectstack-data/rules/datasources.md: the credential section now says a
code-declared datasource reads its password at connect from the variable derived
from its name while the host switch OS_DATASOURCE_ENV_CREDENTIALS is on (the CLI
host defaults it on, a library composition is off, on/off only), that a declared
credentialsRef or connector still wins with no fallback, and the operator's
caution that an installed package may declare a datasource of any free name and
so read that variable. The added text is paid for under the token ratchet by
deleting restatements in the same file; the file lands at 910 of 911 tokens.

ADR-0015: the Amended-by line, an inline correction of the D7 note's "that stays
true for a database", the section 10 credentials bullet, and a dated amendment
note under section 10 recording the route, its guards, the failure text and the
trust consequence. ADR-0062 D3: a dated amendment recording the slot's place in
the connect's resolution order.

A patch changeset for @objectstack/skills, which ships skills/**.

Claude-Session: https://claude.ai/code/session_01FEkQ5phX4SAaFQKww3TfKb
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract-review-claim: session_01L9qibw58ehQ2gRHP2h2Puz · head 17988ade2c9a3e191a12c51a7419669c612ce2a5 · 2026-10-31T15:47Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 17988ade2c9a3e191a12c51a7419669c612ce2a5
Local-runs: none
Scope: docs/adr/0015-external-datasource-federation.md, docs/adr/0062-external-datasource-runtime.md, skills/objectstack-data/rules/datasources.md + packages/services/service-datasource/src/datasource-env-credential.ts, packages/services/service-datasource/src/datasource-connection-service.ts, packages/services/service-datasource/src/datasource-admin-plugin.ts, packages/services/service-datasource/src/datasource-admin-service.ts, packages/services/service-datasource/src/datasource-credential-migration.ts, packages/services/service-datasource/src/default-datasource-driver-factory.ts, packages/cli/src/commands/serve.ts, packages/cli/src/commands/start.ts, packages/cli/src/commands/dev.ts, packages/cli/src/utils/datasource-env-credentials-default.ts, packages/runtime/src/default-datasource-plugin.ts, packages/runtime/src/declared-datasources.ts, packages/spec/src/data/driver/common.zod.ts, packages/spec/src/data/datasource.zod.ts, packages/drivers/driver-odata/src/plugin.ts, packages/drivers/driver-odata/src/odata-driver.ts, skills/objectstack-data/SKILL.md, skills/objectstack-data/references/_index.md, skills/objectstack-data/evals/hooks-security-seeds-datasources.json, .changeset/28209-skills-code-datasource-env-credential.md, scripts/check-empty-changeset.mjs, packages/skills/scripts/sync-catalog.mjs, AGENTS.md

Face: governed rules text (skills/** ×1, docs/adr/** ×2; 3 of 3 governed paths). The diff is prose only — no schema, export, key, value, error code or runtime line moves — so the review is whether each taught sentence matches how main (merge-base 5da3a8ce, and no commit since has touched the cited code) resolves a credential for a datasource declared in code against one saved through Setup. Every claim below was read in the cited code with git show; nothing was built or run.

① Derived judgments

Acceptance set and public faces: unchanged. No packages/spec path, no exports, no .d.ts. Clause-②: no on the PR body and the changeset is correct. The published surface that does move is @objectstack/skills (skills/** is copied by sync-catalog.mjs into dist/skills, the package's only files entry besides README/CHANGELOG), so the derived judgments are the taught sentences, one by one.

skills/objectstack-data/rules/datasources.md, "Credentials — fail-closed" — each sentence against main:

  • "A code-declared datasource reads one at connect from OS_DATASOURCE_{NAME}_PASSWORD (NAME = the name upper-cased)" — correct. datasourceEnvCredentialVar is OS_DATASOURCE_ + name.toUpperCase().replace(/[^A-Z0-9]/g, '_') + _PASSWORD; the schema name regex is ^[a-z_][a-z0-9_]*$ (datasource.zod.ts), on which "upper-cased" is the whole rule (the only non-alphanumeric a valid name can carry is _, mapped to itself). The slot is consulted only on a declared-auto connect whose name is in the host's code-datasource-names set and that is not asDefault (envCredentialVarFor; the !opts.asDefault guard) — i.e. exactly the boot connect the authoring agent's datasource meets.
  • "while the host switch OS_DATASOURCE_ENV_CREDENTIALS is on (os serve/start/dev default it on; a library composition is off; on/off only)" — correct. resolveDatasourceEnvCredentialsSwitch: unset/empty ⇒ off, on/off case-insensitive, anything else throws; DatasourceAdminServicePlugin.init() calls it and spreads envCredentials only when true, so a bad value refuses the boot. serve.ts calls stampDatasourceEnvCredentialsDefault(process.env, …) before the admin plugin is built (stamps on only when unset/empty); start.ts and dev.ts spawn serve. DatasourceConnectionServiceConfig.envCredentials is optional and absent by default, so a composition that never runs that line is off.
  • "Operator caution: an installed package may declare a datasource of any free name and so read that variable" — correct and stated as a caution, no recipe. declared-datasources.ts calls contributeCodeDatasourceNames(ctx, names) for an installed package's datasources, so under os serve they are in the set isCodeDatasource reads.
  • "external may instead name one channel, which wins and never falls back to the variable" — correct. The slot sits in connect() under if (!credentialsRef && connector === undefined && !opts.asDefault), after the fail-closed credentialsRef resolution and the D7 connector borrow, each of which returns failed-credentials before the slot is reached.
  • The kept credentialsRef / connector bullets and the fail-closed paragraph are unchanged text, still true on main.

The five deletions, each with a home (the ratchet payment): the datasource.zod.ts pointer — references/_index.md:12; "(default: 'default')" — SKILL.md field table row datasource; // writable: true and "Otherwise every write … is rejected." — the Writes section's two-gate sentence still states the rule; the "Column mapping" ✅ bullet — the example's columnMap line shows the key and its direction, and the ❌ field.columnName rule stays; "(at defineStack, else at connect)" and "and its federated objects registered," — timing clauses whose rule survives in the sentence they hung on. No rule lost; no line re-wrapped to gain room.

ADR-0015 — the Amended-by entry, the D7 inline correction, the §10 bullet and the §10 note:

  • Form: the header already lists four amendments in the same "#NNNN (date, ruling) — scope — see the note" shape; the new entry matches. The Status line stays Accepted — correct, the amendment narrows nothing and neither ADR is superseded (ADR-0062 "supersedes the runtime portions of ADR-0015 §18" only, and names ADR-0015 as the canonical binding decision; the PR keeps that split: the operator caution lives in 0015 §10, 0062 D3 points to it). The §10 note uses the blockquote-with-bold-lede form of the three existing notes; the inline *(Amended …)* on the bullet copies the D7 precedent on the same bullet.
  • Facts: variable derivation and example — verified above. "the password for postgres, mysql and mongodb, the authToken for turso" — default-datasource-driver-factory.ts maps spec.secret to password in the pg, mysql and mongo arms; the migration table in datasource-credential-migration.ts records turso's slot as authToken, read from spec.secret. Switch semantics, init() refusal, os serve stamp, start/dev — verified. "never a record's origin" — servedOrigin and envCredentialVarFor both read the provenance set only. "default keeps OS_DATABASE_URL (an asDefault connect never reads the slot)" — the guard. Failure text — verbatim with main: the driver's errMsg(err) followed by (note: datasource 'NAME' is defined in code and reads its credential from OS_DATASOURCE_NAME_PASSWORD at connect, which is not set on this host), verdict failed-degraded. Collision — the second connect is refused failed-credentials with the message naming both datasources and the variable, before read; a failed attempt deletes its claimedVariable. "Setup → Datasources form refuses a code-origin datasource, and nothing else mints one" — the create door throws a code-defined datasource owns this name (read-only), the credential-migration door refuses origin !== 'runtime', and the saved-datasource Test door probes without the connection service, so no door binds a handle for a code datasource. Spec sentence — CODE_DEFINED_CREDENTIAL_ROUTE is appended at five refusal prescriptions and the password key description in common.zod.ts and the two password guidance entries in datasource.zod.ts: 5 + 1 + 2, as the note says. "Not taken" — matches the guard list in datasource-env-credential.ts's own header.
  • One loose word, not a must-fix: "A plugin driver that resolves its credential per request (the OData driver resolves credentialsRef that way)". driver-odata/plugin.ts resolves the handle at the first request and again on a 401, not on every request. The operative claim — the OData driver never consumes spec.secret, so the slot cannot feed it — holds (plugin.ts reads only the secret store or the connector handle).

ADR-0062 D3 amendment: the five conditions (neither channel declared, not asDefault, trigger declared-auto, name in the code set, host opted in) are the exact predicate of connect() plus envCredentialVarFor. The sentence "Default behavior below gains one line: …" declares the growth in the note without editing the normative list — the same form the ADR's #4462 amendment used ("The D2 gate grows (d)" while the D2 paragraph still lists (a)–(c)); consistent with precedent, and the PR body says the normative bullets were left alone on purpose.

② Semver level

@objectstack/skills patch — correct, and the changeset is owed. skills/** ships in @objectstack/skills (dist/skills, produced by sync-catalog.mjs, listed in files), so AGENTS.md's Post-Task Checklist item 3 applies ("⛔ never skip-changeset: that label is for a diff that publishes nothing from any released package") and scripts/check-empty-changeset.mjs says the same in its failure text ("a skills/** change does: it ships in @objectstack/skills"). The order's skip-changeset line was wrong on this point; the dev named the deviation rather than following it, which is what os-dev.md asks. The changeset names the right package, the right bump, and carries both declaration lines (Clause-②: no, Metadata-protocol: none) matching the PR body; its shape is the shipped precedent .changeset/26475-skills-teach-queues-assignment-rules.md. No #NNNN in the skills text (check:pm-skill-id-lint reported clean); the changeset body's #NNNN references are allowed there. No ceiling moves (910 of 911 tokens).

③ Boundary flags

  • open_questions: none in the os-dev-report. The one deviation (changeset vs skip-changeset) is answered in ② — the PR is right; the order was not.
  • out_of_scope_findings — skills/objectstack-data/evals/hooks-security-seeds-datasources.json case id 3: confirmed, and it now contradicts this PR's text. The eval's expected_output answers "A defineDatasource entry (credentialsRef, never inline credentials)" for a code datasource over a Postgres table; the rules file this PR ships now leads with the environment route for a code-declared datasource, and ADR-0015's note records that nothing mints a sys_secret: handle for one. The evals ship: sync-catalog copied 95 files, which is the 67 non-eval files plus the 28 under evals/, so the published bundle carries both texts. The reader who meets the contradiction is an eval grader or a skills maintainer reading the model answer, not an authoring agent following rules/datasources.md; the case's must_contain does not require credentialsRef, and columnMap / schemaMode it asserts are still taught. So: a follow-up card on the skills lane to re-word case 3's model answer is warranted; it is not a must-fix for this PR, whose card and claim name three texts.
  • Author: objectstack-fleet[bot] — no author note owed.
  • Tier H: the PR is draft, mergeable_state: blocked, no auto-merge armed, no skip-changeset label (labels on it are the labeler's). It waits for the maintainer's own approval; this record is the review half.
  • CI at the time of this read: 19 success, 13 skipped, 7 still in progress (Test Core 1/8 and 2/8, three Type Check jobs, two Lint jobs); nothing red. The landing precheck's all-green reading is the seat's to take at landing time.
  • Security: no credential value, connection string or example password in the diff; variable names only. The trust consequence is a caution with no recipe in all three texts.

④ Design judgment

  • skills/objectstack-data/rules/datasources.md against the card's acceptance ("teach exactly what landed, including the trust note", no #NNNN, under the ratchet) and the spec on main: holds. The sentence an authoring agent follows (OS_DATASOURCE_{NAME}_PASSWORD, switch, CLI default, precedence) is exactly CODE_DEFINED_CREDENTIAL_ROUTE's prescription in common.zod.ts and the code behind it. Nothing undelivered is taught; the collision refusal and the unset-variable note are left to the ADR, which is right for this reader (no valid pair of names collides, and the runtime message names the variable). One optional strengthening for a later edit, not owed here: the credentialsRef bullet still reads as a channel a code author could pick, while a handle exists only for a datasource saved through Setup; the lead sentence already steers the reader to the environment route, and the schema does accept credentialsRef in every schemaMode, so the text is not false.
  • ADR-0015 §10 note against its first consumer and execution plan: the consumer is DatasourceConnectionService.connect() as shipped on main; no plan is owed because the note records landed behaviour, and it says so (ruling, PR, and the #28325 corrections named in the lede). The guards it lists are the guards the code enforces, and the "not taken" list is the implementation's own.
  • ADR-0062 D3 amendment against the runtime it governs: the resolution order and the five conditions are the code's predicate; the pointer to ADR-0015 for the operator caution keeps the two ADRs' division of labour (binding decision in 0015, runtime decision in 0062). The declared-growth form matches the ADR's precedent.
  • Nothing taught would mislead a third-party agent installing the published skills; the one contradiction it would ship is the pre-existing eval case 3, carried as a follow-up above.

No must-fix.

Implemented-by: claude/project-thread-c92hml
Reviewed-by: session_0186vfjHHMsRXC9EUT9hTFMo

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

席位核对

ACCEPT · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-31T15:56Z · head 17988ade · #28209

The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」. This PR is Tier H: that authorisation does not reach it. It stays draft until the maintainer approves.

  1. Shape:
    • A draft against main with no assignees, Fixes #28209, and a patch changeset for @objectstack/skills. No skip-changeset label.
    • check-governed-merges --pr 28432 reads ⛔ GOVERNED, docs/adr/** ×2 and skills/** ×1, 113 changed lines. Landing tier H(人合).
    • The claim (6545227271) names this PR's branch, claude/project-thread-c92hml.
  2. Against the order:
    • The datasources rule teaches the environment route for a code-declared datasource. It reads one password at connect from OS_DATASOURCE_{NAME}_PASSWORD while the host switch OS_DATASOURCE_ENV_CREDENTIALS is on (os serve / start / dev default it on). Each sentence matches main's connect path, precedence and refusals, as the contract review walks.
    • ADR-0015 and ADR-0062 carry dated amendment notes in the repo's amendment form. They record that nothing mints a sys_secret: handle for a code datasource.
    • Security discipline holds. The text names variable names only, with no value, connection string or example password, and the trust caution carries no recipe.
    • The order's skip-changeset ruling was wrong, and the dev was right to depart from it. skills/** ships inside @objectstack/skills. The seat has corrected its order template.
  3. Contract review: owed at CONTRACT_REVIEW_TIER (governed rules text). PASS on this head, 6545658053. One nit, not blocking: ADR-0015 says OData resolves "per request", where main resolves on the first request and again on a 401.
  4. Gates:
    • Locally, the dev ran 49 derived families: 47 exit 0 and 2 NOT MEASURED (check:dual-build-cjs-loads, check:lean-entry-closure, which need a full build; CI runs them).
    • CI on head 17988ade: 29 success, 13 skipped, 0 red. The state is clean.
  5. Overlap: no other open PR touches the two ADRs or skills/objectstack-data/rules/datasources.md.
  6. Follow-up filed: skills(objectstack-data) eval: hooks-security-seeds-datasources case 3 still answers credentialsRef for a code-declared datasource #28435. The eval hooks-security-seeds-datasources.json case 3 still answers credentialsRef for a code-declared datasource, which this PR's rule now contradicts. It is outside this card's file surface, so it is not edited here.
  7. Next:
    • ⛔ No seat flips it ready, enqueues it or arms auto-merge. The maintainer's approval (or merge) is the review record.
    • After it merges, the landing record goes on the card and the claim is released.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract-review-claim withdrawn: session_01L9qibw58ehQ2gRHP2h2Puz · head 17988ade2c9a3e191a12c51a7419669c612ce2a5 · 2026-10-31T16:12Z

The claim 6545612522 above was posted by the maintainer's contract-review thread, not by seat 2, and it was signed with the card's seat name by mistake. Seat 2's own record 6545658053 (PASS on this head) stands as the review of record; this thread produced no record and reviews nothing further on this head. Its later rows sign as triage#1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills + ADR-0015/0062: teach the code-defined datasource credential route (OS_DATASOURCE_<NAME>_PASSWORD) once #28208 lands

2 participants