Skip to content

feat(spec)!: the build doors refuse a builtin node config value its executor contract refuses, with its location - #21974

Merged
objectstack-fleet[bot] merged 11 commits into
mainfrom
claude/issue-21898-builtin-node-config-values-judged
Oct 7, 2026
Merged

objectstack-fleet[bot] merged 11 commits into
mainfrom
claude/issue-21898-builtin-node-config-values-judged

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #21898
Clause-②: yes (narrowing)

Merge gate (the ruling's timing): this PR merges only once .objectui-sha on main is at or after 5ba255538a (objectui#11670, the Studio designer storing a screen field's Min / Max as numbers). At this base the pin is 0abd4f9f87, which does not carry it. This PR does not move the pin.

At landing: both gates are met.

main came in by two pure merges, 42ce99cf91 and 00bf19bdb5. Each tree equals its git merge-tree.

One test commit, c5545a54a6, adds a 16th file: packages/services/service-automation/src/builtin/notify-template-slots.test.ts.

Draft. Patch round 1 re-judges the cross-lane fixtures that the claim revision 6012822762 declared (#6021 comment 6012831917; #6367 comment 6012842570). See "Cross-lane fixtures re-judged" below. No source line moved in any of those packages.

The build doors now refuse a value that a builtin node's executor contract refuses, with its location, at FlowSchema.parse, objectstack validate and objectstack compile. Ruling 6010677104 (A) gives the pin, the measured pair: create_record with config.outputVariable: 42, and a screen field with a string min. Each is refused at save with its location. Until now both passed every build door and registered, and then every run that reached the node failed at the executor's contract parse.

What changes

packages/spec/src/automation/flow-node-config-refusals.ts: the builtin executor-contract arm of flowNodeConfigRefusals is no longer presence-only. A contract issue at a key the author wrote is now refused with the existing closed-set code node-config-refused-by-contract, params: { nodeType, key }, anchored at the key. It is the same code, and the same message builder, the approval contract uses. No new code joins FLOW_SLOT_REFUSAL_CODES.

The refusal is kept only where the build knows what the run will parse (builtinValueJudged). Each carve-out sits where another judge owns the finding, or where the run may parse something other than what was authored:

  • Key membership is not judged. That covers an undeclared key (unrecognized_keys) and a tombstoned one (a retiredKey(), invalid_type expecting never). Registration refuses an undeclared key against the descriptor, with its own prescriptions. The lint names the retired script keys. The D2 layer rewrites retired spellings first at the two doors that convert.
  • A region slot, the slot itself included (try: 5, a one-branch parallel). Region shape belongs to validateControlFlow; region nodes are judged as graphs of their own.
  • A predicate or value ledger slot, at or inside it: a screen field's visibleWhen, and a CRUD fields value. predicateSlotRefusal judges the first (its non-strings are left to registerFlow and validate by ruling, per the flow.zod.ts note). The value-envelope pass judges the second.
  • http.signingSecret. A secret held in the credential channel replaces the literal before the parse.
  • ⛔ A value carrying a {token} anywhere inside it. It is never refused for its pre-interpolation type. The pattern is the interpolator's own, which also matches the double-brace and dollar-brace spellings.

getBuiltinNodeConfigContracts() keeps its export, its shape and its 13 entries, and the lazy-build cycle note stands. The approval arm is unchanged and still judged whole. The docblocks that said "presence-only" moved: the module header, the judge's docblock, absentAt, two blocks in flow.zod.ts, and the approval test's control title and header.

Built-ins not judged whole, each with its reason

  • http: its executor parses after interpolating the whole config. So a value is judged only when nothing inside it carries a token (interpolation is then the identity). A rule is judged only when the whole config carries none. signingSecret is never judged.
  • loop: its executor parses only when there is a body (parsedWhen). A legacy flat-graph loop is judged for nothing. A loop with a body is judged on collection, iteratorVariable, indexVariable and maxIterations.
  • The region containers, loop (body), parallel (branches) and try_catch (try, catch): they hold regions, which are judged as graphs of their own. Each container is judged on the keys beside its regions, such as try_catch's errorVariable and retry. parallel has only its region slot, so it is judged for presence alone.

Every other builtin is judged on every present value: get_record, create_record, update_record, delete_record, notify, screen, script, subflow and map.

Census (round 1, report 6006631317)

The census took every builtin node config at 833d57c9cf. For each it listed what the planned arm refuses, using the arm as its predicate. A lit control (planted file) found all 6 planted refusals and exempted both planted tokens.

  • This repository: 96 real-writer nodes in examples, docs, skills and packages/qa. None is refused. 190 template strings are counted separately, all in string-typed slots.
  • hotcrm 4054ec2680: 138 nodes, none refused.
  • objectui at the pin, static: 94 nodes, none refused outside tests.
  • The one real writer: objectui's designer stored a screen field's Min / Max as strings. objectui#11670 is its fix, hence the merge gate.

After this change I re-ran the census with the implemented judge in place of the predicate. On every statically evaluable node it agrees with the predicate. The only differences are values the walker cannot evaluate.

Census, extended in patch round 1 to helper calls, same-file consts and property assignments. The walker now also reads three shapes:

  • a call f(…, 'TYPE', …, { … }), taking the first object after the type as the config;
  • an identifier that resolves to a same-file object literal;
  • an assignment into ….config or ….config.KEY.

A lit control found a helper-call refusal, a const-resolved refusal and a rule-array assignment, and passed a token.

  • This repository at d1c7d8d392: 1065 configs (839 literal nodes, 224 helper calls, 2 JSON), plus 70 assignment sites. The judge refuses static values in 27 rows. Each is one of four things:
    • a pin of this change;
    • a door half added in this round;
    • a test that never reaches the judge (lintFlowCredentialLiterals, validateFlowNodeWrites, lintFlowPatterns, resolveFlowNodeExpressions), green;
    • one of the fixtures re-judged below.
  • Assignments: none writes a refused value into a builtin config, by hand-reading all 53 literal assignments.
  • hotcrm 4054ec2680: 138 configs, 0 refused.
  • What the walker still cannot evaluate:
    • 64 configs that hold a value from an import, a function call, a spread of a non-local object or a template literal with substitutions;
    • configs built inside a function body (configFor(type, …)) or a loop over a sweep;
    • the node type of an assignment target, so assignments are triaged by hand;
    • JSON or YAML nodes without an id or label;
    • prose in docs.

Reproduction, before and after (a scratch copy of examples/app-showcase, removed afterwards)

variant 833d57c9cf (base) this branch
control, unedited validate 0 · compile 0 validate 0 · compile 0
create_task outputVariable: 42 validate 0 (✓ Validation passed) · compile 0, artifact carries 42 validate 1 · compile 2, custom at nodes.1.config.outputVariable, no artifact
a screen field min: '1' (engine: registers, run fails) validate 1 · compile 2, custom at nodes.1.config.fields.0.min
token control, get_record limit: '{inquiry_cap}' — validate 0 · compile 0, artifact carries the token

Every edit was proved on disk with grep -c, anchor 1→0 and injected 0→1. The validate door now reads: "This create_record node's config is refused at outputVariable by the create_record contract: Invalid input: expected string, received number. Its executor parses the config against that contract before it does anything else and refuses the node on any finding, …".

At base, at the engine (built service-automation): outputVariable: 42 registers, then runs 1 and 2 each fail with create_record 'mk': config does not satisfy the create_record contract — config.outputVariable: … and 0 inserts. The designer-shaped screen node registers, then its run fails at config.fields[0].min.

Pins (flow-builtin-node-config-values.test.ts, 44 tests)

  • The measured pair, at FlowSchema (custom at nodes.1.config.outputVariable and at nodes.1.config.fields.0.min), with the judge's code, params and path. It is also refused inside a loop body, at nodes.1.config.body.nodes.0.config.outputVariable.
  • One refusal per judged builtin at a typed key. 24 probes cover every type with a value key: limit, multi, severity, a notify rule, timeoutMs, durable, headers.X-Kind, mode, fields[0].required, an empty function / flowName, collection, maxIterations, errorVariable, retry.maxRetries and others.
  • Controls. A valid node of each of the 13 types parses. Tokens in typed slots are never refused, in single-brace, double-brace and dollar-brace spellings, and for each one the contract itself is shown to refuse the string. A token-free sibling slot is still judged. Each carve-out above holds back while the contract refuses. A legacy loop is not judged. A missing key keeps its presence code.
  • Doors. defineStack (STACK_SCHEMA_INVALID / 422 at flows.1.nodes.1.config.outputVariable), ObjectStackDefinitionSchema (both pins), the registered flow type schema the save door uses (the screen pin) and the artifact parse. The CLI doors are in the table above.

Ablation (reverse verification). I committed first, then used scripts/ablation-replace.mjs to restore the presence-only line (if (!absent && !whole) continue;): anchor 1→0, blob 01e47e2d7e35→2d67f51da4c4. The subject resolves through src by relative import, so no build was needed.

  • Predicted: 40 red (36 in the new file, 1 in flow-node-config-required.test.ts, 3 in flow-write-node-stored-metadata-target.test.ts).
  • Observed: Tests 40 failed | 129 passed (169), 36 / 1 / 3 as predicted.
  • Restored: blob equal to HEAD and git diff HEAD empty.

A second ablation deleted the token exemption line. Predicted 1 red (the token control); observed Tests 1 failed | 70 passed (71), then restored the same way.

The ADR-0087 kit

  • D3 entry: entries/semantic/18.flow-builtin-node-config-values-refused.ts. Its registry.ts region was regenerated by gen:migration-registry.
  • Step-18 rationale: a fragment at order 85. I re-read origin/main at 230e4944b0 just before opening: the highest order there is 84, and this id is absent.
  • No tombstone, no D2 conversion. No key is removed, and the platform cannot know the value the author meant.
  • Changeset: one BREAKING minor for @objectstack/spec, with the registered marker and the Clause-② line. check-adr-0087-registration passes.
  • Regeneration: check:generated reports all 15 artifacts up to date. The public exports did not change.

Fixtures re-judged in this PR

  • spec/.../flow-node-config-required.test.ts: a control pinned "a present wrong-typed value is not refused". That is exactly the branch removed, so the control is replaced. It now asserts the value arm's code, and that this rule still reports absence only.
  • spec/.../flow-write-node-stored-metadata-target.test.ts: the "dynamic objectName" control included an expression envelope in objectName. The CRUD contract declares objectName a string, so the run refused that envelope too. The template cases keep their control. The envelope now asserts exactly the value arm's refusal, and still none from the write-target arm.
  • spec/.../flow-approval-node-config-contract.test.ts: only the builtin control's title and header wording; the assertion is unchanged.
  • lint/src/validate-expressions.test.ts:2360, declared on [PM seat] domain:devx @ objectstack — 🟢 baozhoutao · session_01VDtqoecgES7ScQYGbFVDRv · R9 · landed 3 · #20004 awaits skip-changeset · in flight 0 #6023 in comment 6011223490: the screen fields = 'nope' fixture now expects exactly the screen contract's refusal at config.fields. No packages/lint source line moved.

Cross-lane fixtures re-judged (patch round 1, declared on their lanes)

Each of these fixtures registered or saved a flow carrying a value its contract refuses, so every one of those flows also failed at its first run. The round-1 census missed them because each config arrives through a helper argument or a property assignment. The step-7 suites caught them.

  • service-automation, config-parse.test.ts (the tests formerly at :118, :129, :184, :193 and :317) and notify-node.test.ts (formerly :274). Each test keeps the subject its title names: the executor's execute-time parse.
    • Each drives that parse past the doors the way the suite already does for a key left out. It registers a value the contract accepts, then writes the refused value into the stored flow before the run. That is the new runPatched beside the existing runStripped; in notify-node, it is the stored-flow edit its "no recipient" test already uses.
    • Each also gains the door half: registration refuses the value at its key: refused at limit, timeoutMs, mode, flowName and template.
    • No test was converted to assert only the registration refusal. No service-automation source line moved.
  • metadata-protocol protocol-publish-drafts-advisories.test.ts:205, and objectql publish-meta-response-conformance.test.ts:413 and save-meta-response-conformance.test.ts:298. The "clean" flow's delete_record filter moves from the rule array to the record form the contract declares: { created_at: { $lt: '2020-01-01' } }.
    • The bounded-write advisory (flow-multi-write-unfiltered, lint-flow-patterns.ts filterCarriesNoCondition) stays silent. Measured: reduceFilterVerdict answers 'clause' for the record form, so a condition is written.
    • The rule array was silent for a different reason: that rule skips a non-object filter, which the contract refuses outright.
    • Each test still asserts what its title says.
    • Precondition, checked read-only first: no real writer saves a CRUD filter as a rule array.
      • objectui at 0abd4f9f87: the designer maps the slot (descriptor type: 'object', additionalProperties: true) to its keyValue widget, which commits a record. It keeps an array only when an array is already stored, and then in its { variable, value } form; it never writes a rule array.
      • The round-1 corpora (examples, docs, skills, packages/qa) and hotcrm 4054ec2680: 0 CRUD-node array filters, by an AST scan with a lit control. The 70 array filters found are page, view and API filters.

Verification (at 22f54b0738, after merging origin/main c9761cd2fb)

Tests

  • @objectstack/spec: full suite 673 files / 19431 passed / 1 todo, exit 0. typecheck exit 0 (check:test-typecheck debt held). check:generated: all 15 up to date.
  • Cross-lane:
    • @objectstack/service-automation: 173 files / 2112 passed, typecheck 0. The ledger trio (node-config-contract-ledger, config-expression-ledger, node-config-required-keys) is green inside it.
    • @objectstack/metadata-protocol: 218 passed / 3 skipped files, 27996 passed / 19 skipped tests; typecheck 0.
    • @objectstack/objectql: 378 files / 7507 passed; typecheck 0.
    • @objectstack/lint: 119 files / 5629 passed; typecheck 0.
    • @objectstack/cli, --project unit: 259 files / 3786 passed.
  • A full turbo build ran first (--filter='!@objectstack/docs', VERDICT 0), so every suite reads a current dist/.

Gates

  • dispatch-gates --ran at 22f54b0738: 96 derived, 96 run, 0 NOT-MEASURED, 0 UNRUN.
  • check-engine-split-ratio --days 90 first refused on the shallow clone (exit 2). I deepened with git fetch --shallow-since=2026-07-01; it then exits 0, with the ratio at 98.4% and the oldest visible commit at 2026-07-01, before the window.

ESLint, narrowed to this PR's own changed files, at 22f54b0738:

  1. Population: ESLint's own isPathIgnored reports all 14 changed .ts files as linted.
  2. Count: --format json reports 14 files, 0 errors and 0 warnings.
  3. Untouched files: eslint.config.mjs enables no type-aware linting (no parserOptions.project), so this diff cannot change the result for any file it does not touch.

Declared to CI: the full pnpm lint, the dogfood suite, the cli integration tier, and every package not named above.

Acceptance notes (not filed)

  • The schemaless KEY half. A script (and by reading, subflow) node with an undeclared config key passes FlowSchema and validateStackExpressions, then registers, then fails every run. registerFlow's key check skips schemaless types, and this arm judges no key membership. It belongs to this card's family, key half; it was measured at the functions the doors call, at 833d57c9cf. Carrier: none.
  • A token in a non-string slot stays run-only, on purpose. A non-http builtin parses its raw config, so get_record limit: '{n}' or a screen field min: '{m}' is still refused at every run. The ⛔ above keeps it out of the build doors. Carrier: none.
  • Two spec-side tables have no reconciler. PARSED_AFTER_INTERPOLATION (http) and RUN_RESOLVED_KEYS (http.signingSecret) are new private tables in the judge. service-automation's ledger reconciles the contract map against the executors' parseNodeConfig calls but reads neither table. A new after-interpolation executor or credential slot would have to be added here by hand. Carrier: none.
  • CLI output (existing behaviour, unchanged). When defineFlow throws while the CLI loads its config, the CLI prints the raw ZodError JSON, with the path relative to the flow and no flow name or file.

Generated by Claude Code

claude added 4 commits October 6, 2026 07:15
…xecutor contract refuses

The executor-contract arm of flowNodeConfigRefusals stops being
presence-only: a present value a builtin node's contract refuses is
refused at parse as node-config-refused-by-contract, anchored at the
key, wherever the build can know what the run parses. A value carrying
a token, key membership, region slots, predicate and value ledger
slots, and http's signingSecret are left to the judges that own them.

Adds the D3 entry flow-builtin-node-config-values-refused (protocol 18,
rationale order 85) and its BREAKING minor changeset.

Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ
Co-authored-by: Claude <noreply@anthropic.com>
…value as unjudged

flow-node-config-required's presence control and the write-target arm's
envelope control each asserted that a present value its contract refuses
passes the parse; the value arm now refuses both under
node-config-refused-by-contract, so each control states that instead.
The lint fixture with a non-array screen `fields` (declared on the lint
lane) now expects exactly the screen contract's refusal at config.fields.

Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 17 documentable anchor(s).

7 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/error-catalog.mdx (via invalid_type (literal, a string literal in builtinValueJudged))
  • content/docs/api/error-handling-server.mdx (via invalid_type (literal, a string literal in builtinValueJudged))
  • content/docs/automation/flows.mdx (via FlowSchema (symbol, a top-level const), invalid_type (literal, a string literal in builtinValueJudged), signingSecret (literal, a string literal in RUN_RESOLVED_KEYS))
  • content/docs/concepts/metadata-lifecycle.mdx (via signingSecret (literal, a string literal in RUN_RESOLVED_KEYS))
  • content/docs/deployment/cli.mdx (via invalid_type (literal, a string literal in builtinValueJudged), unrecognized_keys (literal, a string literal in builtinValueJudged))
  • content/docs/protocol/objectql/types.mdx (via unrecognized_keys (literal, a string literal in builtinValueJudged))
  • content/docs/protocol/objectui/concept.mdx (via invalid_type (literal, a string literal in builtinValueJudged))

⛔ 4 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via FlowSchema (symbol, a top-level const), invalid_type (literal, a string literal in builtinValueJudged), unrecognized_keys (literal, a string literal in builtinValueJudged))
  • content/docs/releases/v17/17-4.mdx (via FlowSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-6.mdx (via invalid_type (literal, a string literal in builtinValueJudged), signingSecret (literal, a string literal in RUN_RESOLVED_KEYS))
  • content/docs/releases/v17/17-7.mdx (via FlowSchema (symbol, a top-level const), signingSecret (literal, a string literal in RUN_RESOLVED_KEYS))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 6 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 3d9188502e1b07ae70df8b0b5e733fce44a74cfa → packageMentionDocs.

Which tree this was computed on

This run read content/docs from ea7ec58e2f22c93fb209410d91714fadab5e7409 — the merge of head c5545a54a61f33284db8ebddd8cb981a115cd56b into base 3d9188502e1b07ae70df8b0b5e733fce44a74cfa, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ea7ec58e2f22c93fb209410d91714fadab5e7409 && git checkout ea7ec58e2f22c93fb209410d91714fadab5e7409
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3d9188502e1b07ae70df8b0b5e733fce44a74cfa c5545a54a61f33284db8ebddd8cb981a115cd56b && git checkout -B drift-repro 3d9188502e1b07ae70df8b0b5e733fce44a74cfa && git merge --no-ff c5545a54a61f33284db8ebddd8cb981a115cd56b

node scripts/docs-audit/affected-docs.mjs --json 3d9188502e1b07ae70df8b0b5e733fce44a74cfa

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 3d9188502e1b07ae70df8b0b5e733fce44a74cfa → pass the list as
args.docs, on the commit named under Which tree this was computed on.

claude added 2 commits October 6, 2026 08:56
…tures that carried a value the build doors now refuse

The execute-time parse tests in config-parse and notify-node now pin the
door half (registration refuses the value at its key) and still reach
the executor's parse the way the suite already does for a key left out:
register a value the contract accepts, then write the refused one into
the stored flow before the run. The "clean" advisory flows in
metadata-protocol and objectql write their delete_record filter in the
record form the contract declares instead of a rule array.

Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ
Co-authored-by: Claude <noreply@anthropic.com>
…polates before its parse

The changeset's FROM -> TO rows and the D3 entry's replacement told an
author to write a {token} template in limit or maxIterations, but
get_record and loop parse their config as authored, so such a value
passes the build doors and fails every run. A number or boolean slot
outside http now takes a literal only, http's slots keep the sole-token
form, and the "still accepted" token bullet says the hold-back is no
promise the value runs. The step-18 fragment says the same; the
registry region is regenerated.

Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ
Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 6, 2026
…iltin-node-config-values-judged

Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ
Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 7, 2026
claude added 2 commits October 7, 2026 14:30
…iltin-node-config-values-judged

Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ
Co-authored-by: Claude <noreply@anthropic.com>
…registration refuses, the executor refuses past the doors

Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 7, 2026 16:09
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 7, 2026 16:09
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit ac9f8bd Oct 7, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21898-builtin-node-config-values-judged branch October 7, 2026 16:48
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/l tests tooling

Projects

None yet

2 participants