Skip to content

feat(verify): the handle fronts the automation engine's condition evaluator - #22553

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22301-evaluate-condition
Oct 10, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22301-evaluate-condition

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #22301

Clause-②: yes (widening)

Item 8 of the card ("automation.evaluateCondition is not fronted"): the verify handle gains a door to the automation engine's own condition evaluator. Items 5, 6 and 7 and item 1's remaining composition gap stay open on the card; this PR touches none of them.

Premise, read on origin/main 86bf9ed7d5 before any edit

  • AutomationEngine.evaluateCondition exists at packages/services/service-automation/src/engine.ts:12225. It takes a condition (a string, or an envelope { dialect?, source?, ast? }) and a Map of variables, and returns a boolean.
  • It is not on the slot's contract: IAutomationService (packages/spec/src/contracts/automation-service.ts:739) has no evaluateCondition member (grep: 0 hits in that file).
  • The kernel's automation service is the engine instance itself. AutomationServicePlugin registers this.engine under 'automation' (plugin.ts:975), and that is the only producer of the slot in packages/ (grep: 1 hit).
  • The handle had contextFor, hooks.run, hooks.updateWhere, validate, flows.run, flows.resume, actions.run, seed, rows, metadata and tenancy. None reaches the evaluator (evaluateCondition in packages/verify: 0 hits).
  • hotcrm, read-only from a sparse clone at f0afcbd: conditionHolds (test/helpers/verify-stack.ts:184-:188) calls stack.kernel.getService('automation').evaluateCondition(…) by hand. It wraps a string condition as { dialect: 'cel', source } first. It has 28 call sites in 5 test files.

What lands

  • stack.automation.evaluateCondition(condition, variables) resolves the booted kernel's automation service at call time (kernel.getServiceAsync, the lookup the handle's engine doors use). It calls the service's own evaluateCondition and resolves with its boolean, unchanged. There is no second evaluator and no re-derived dialect rule, and no caller is resolved, because the evaluator takes none.

    • condition has the engine's own parameter type, read off the method (Parameters of AutomationEngine['evaluateCondition']), so the door accepts exactly the shapes the engine accepts. It is handed over as given. The engine's own flow sites hand the evaluator an envelope: the start gate wraps a string condition as { dialect: 'cel', source }, the decision executor wraps each branch expression the same way, and an edge's condition already is one. A bare string gets the engine's sniff (CEL unless it carries a {var} hole), the reading a screen field's visibleWhen gets. The JSDoc says which to pass.
    • variables is a plain object. Each own key becomes one entry of the Map the engine takes.
    • Faults are the engine's. A condition the engine cannot evaluate rejects with the engine's own error, never false. No catch is added.
    • No automation service on the stack: the door rejects with the kernel's own branded SERVICE_NOT_REGISTERED error, whose serviceName is 'automation' (isServiceNotRegisteredError from @objectstack/core). No error code is minted in @objectstack/verify, so its error-code ledger row is unchanged and nothing in packages/spec changes. The remedy (automation: true, or the app's requires: ['automation']) is in the JSDoc and the README.
  • The name. automation is the kernel slot the door resolves, and evaluateCondition is the engine method it calls, so the door reads as the call it makes, the way tenancy() is named after its slot. The flows group was the other candidate, but its doors are dispatcher routes run as a caller, and this one is an engine call with no caller.

  • The door is named in handle.ts's door roster, in VerifyStack's member list (harness.ts, docblock only) and in the README's handle section, API list and the automation boot option.

  • .changeset/22301-verify-evaluate-condition-door.md: @objectstack/verify minor, Clause-②: yes (widening).

  • packages/qa/dogfood/test/rls-runner.test.ts (+1, patch round at 1582733139). Its hand-built VerifyStack literal now stubs automation: undefined as never beside the other handle members it does not model. Without it, @objectstack/dogfood#typecheck failed with TS2741 on 79e9053e50. A typed grep across packages/**, examples/**, apps/**, skills/** and content/docs found no other hand-built VerifyStack / VerifyHandle literal.

Evidence, at HEAD 79e9053e50

  • The pins: packages/verify/src/handle.automation-door.test.ts, 6 / 6. Two boots: one whose app declares requires: ['automation'], and one without the service.
    1. A spy on the booted service instance sees one call with the same envelope object and a Map whose entries equal the variables, and its return value is the door's answer.
    2. The truth table over record-shaped variables (record, previous) is true once and false twice, equal to the engine called directly.
    3. An envelope and a bare CEL string answer alike.
    4. A bare {amount} > 100 keeps the template dialect (true), and the spy sees the string as given. The same text in a CEL envelope is the brace trap: the door rejects with the same message the engine throws directly.
    5. A CEL parse error, and a reference to an unbound variable, reject with the engine's own error: the same message as the direct call, not a VerifyRefusal, no code added.
    6. Without the service, the door rejects with the kernel's SERVICE_NOT_REGISTERED error (code, serviceName: 'automation', the brand predicate), with the same message the kernel gives when asked directly.
  • Two ablations, on the committed head, each restored blob-equal to HEAD (node scripts/ablation-replace.mjs, anchor hit 1 -> 0, blob changed, git diff HEAD empty after restore). The subject resolves to src/ (relative imports, no package exports), so no rebuild is involved.
    • A: the door answered around the engine (a stand-in that never calls it). Predicted 5 red and 1 green; observed 5 red, 1 green (the no-service pin, which the kernel answers first).
    • B: the door wrapped a string as a CEL envelope, as the hotcrm helper does. Predicted only the dialect pin red; observed only that pin red (condition failed to evaluate as CEL: Expected COLON, got RBRACE).
  • The type is the engine's. A one-off probe passing 42 as the condition reds tsc with TS2345, naming the engine's parameter type. The probe was removed and the tree is clean.
  • @objectstack/verify in full: 26 files / 209 tests pass, run at 0aac167d63. The one later commit changes a docblock only. Typecheck exit 0, with the new test file in the test program (1 of 26 test files listed by --listFiles).
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 79e9053e50 derives 64 families. 63 ran, each exit 0. pnpm check:dual-build-cjs-loads is NOT MEASURED: it needs a whole-workspace build, which this dispatch excludes, so CI runs it. --ran reconciles 64 of 64, with 0 UNRUN. From the artifact-roster block, 49 of its 52 commands ran, each exit 0. That includes check:error-code-provenance: 337 stamp sites, each listed or waived, and nothing new under @objectstack/verify. The other three are the PR-context guards. They run against this PR once it exists, and their readings go in the card report.

Patch round, at 1582733139:

  • the dogfood closure build was 63/63; pnpm --filter @objectstack/dogfood typecheck and pnpm --filter @objectstack/verify typecheck exit 0; the door file is 6/6.
  • Control leg: deleting the new line turns the dogfood typecheck red with CI's exact TS2741, and the restore was blob-equal.
  • Gates: 68 derived, 67 run and 1 NOT MEASURED (dual-build-cjs-loads), 0 UNRUN; the rosters were 51; CI showed 32 success and 3 skipped.

Acceptance notes

Noted here, not filed:

  • The door types the automation slot as the engine class, because evaluateCondition is not on IAutomationService. This is the same reason engine types the objectql slot as ObjectQL. Another provider under that slot would answer a TypeError at this door. There is one producer of the slot in packages/ today, so this is an observation with no reach.
  • For "no automation service", the two automation surfaces on the handle answer differently, by design. flows.* is a route door, and gets the route's envelope for an empty slot (the dispatcher's 501, read from packages/runtime/src/domains/automation.ts:2374, not measured on a boot here). This door is an engine door, and gets the kernel's in-process error. That is the split the handle already documents between its route doors and its engine doors.

For the repo:hotcrm seat ("done when")

conditionHolds(stack, condition, vars) can move to await stack.automation.evaluateCondition(condition, vars). Two porting differences apply:

  • the helper's string wrap is the start gate's and the decision executor's call-site behaviour, not the evaluator's. To keep modelling those sites, pass the envelope ({ dialect: 'cel', source }), as the helper did;
  • the door resolves a promise. The filter call site at account-name-normalized-match.test.ts:600 needs Promise.all.

Generated by Claude Code

claude added 3 commits October 9, 2026 23:48
…e handle (WIP)

stack.automation.evaluateCondition(condition, variables) calls the booted
kernel's own automation service's evaluateCondition with the condition as
given and the variables as the Map the engine takes, and answers its boolean.

Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF
Co-authored-by: Claude <[email protected]>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 10, 2026
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/verify, touching 4 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/verify/README.md, packages/verify/src/harness.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/releases/v17/17-4.mdx (via evaluateCondition (literal, a string literal in AutomationCondition))

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
  • 2 changed file(s) yielded no anchor (packages/verify/README.md, packages/verify/src/harness.ts) — pages documenting those are invisible to this run
  • 1 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 — 3 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 b53b949a1518ccd5bbfb2f3b8a6fac4eaa9c2da2 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4ceead9431e96d5f3d3f2b02c7dd82563c8d28d1 — the merge of head 15827331397c73330476967e876ed58e31a6f9eb into base b53b949a1518ccd5bbfb2f3b8a6fac4eaa9c2da2, 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 4ceead9431e96d5f3d3f2b02c7dd82563c8d28d1 && git checkout 4ceead9431e96d5f3d3f2b02c7dd82563c8d28d1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b53b949a1518ccd5bbfb2f3b8a6fac4eaa9c2da2 15827331397c73330476967e876ed58e31a6f9eb && git checkout -B drift-repro b53b949a1518ccd5bbfb2f3b8a6fac4eaa9c2da2 && git merge --no-ff 15827331397c73330476967e876ed58e31a6f9eb

node scripts/docs-audit/affected-docs.mjs --json b53b949a1518ccd5bbfb2f3b8a6fac4eaa9c2da2

⚠️ 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 b53b949a1518ccd5bbfb2f3b8a6fac4eaa9c2da2 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…ation member

The hand-built VerifyStack literal in rls-runner.test.ts types every handle
member it does not model as `never`; the handle's new `automation` member
joins them, so the dogfood typecheck compiles again.

Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF
Co-authored-by: Claude <[email protected]>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 15827331397c73330476967e876ed58e31a6f9eb
Local-runs: none

PR #22553 (Part of #22301, item 8), reviewed on the net diff of refs/pm/pr-22553 against its merge-base with origin/main, 86bf9ed7d58cfaf37e48a28a48f5851a17206359 (the branch has not merged main; origin/main sits 4 commits past the base, none touching packages/verify, the dogfood rls-runner test or the engine). Six files, +295 / -7, equal to the PR's file list. Inputs: the card body and its 37 comments, the PR body, its one comment, its file list, the diff, and the head's check-runs. Nothing was built, run, re-run or ablated here.

Check-runs on the head, read at 2026-10-10T01:09Z: 42 of 42 completed, 37 success, 5 skipped (Auto Label and Check PR Size on the second trigger, Console Pin Gate, Build Docs, the opt-in packed-tarball smoke), 0 failure. The seven required contexts (Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard) are each success. Check Changeset and the three PR-context guards are success on both triggers.

① Derived judgments

  1. New required member automation.evaluateCondition(condition, variables) on the exported VerifyHandle interface — a public-surface change in @objectstack/verify. Right. VerifyHandle is exported at packages/verify/src/index.ts:16 and VerifyStack extends VerifyHandle (index.ts:11); bootStack returns { kernel, api, raw, signIn, signUp, apiAs, stop, ...handle } (harness.ts:1295), so the member lands on every booted stack at runtime, not only in the type.
    • Who it widens for: every caller holding a VerifyStack from bootStack / bootStackOnce. Measured on the head across packages/**, examples/**, apps/**, skills/**, scripts/**, content/** and docs/**: about forty stack: VerifyStack annotations and fourteen functions returning a promise of VerifyStack (the dogfood boot() helpers, getSharedShowcase, bootShowcase, verify's own harness.*.test.ts), all of which hold or return bootStack's result. Unaffected, now wider.
    • Who it can break: anything that hand-builds or implements the interface. One object literal typed to it exists in the repo, packages/qa/dogfood/test/rls-runner.test.ts:68 (fakeStack(): VerifyStack), and this diff patches it with automation: undefined as never beside the nine other handle members it already types as never by design (the fake models the HTTP half only). That literal was CI's TS2741 at 79e9053e50; the patch is the right one and the only one owed. The one other fake, packages/qa/dogfood/test/armed.dogfood.test.ts:224, is cast as unknown as VerifyStack, which bypasses structural checking and is unaffected. No implements or satisfies site exists. The objectui sibling imports no @objectstack/verify.
  2. The door's parameter type is the engine's own. Right. AutomationCondition is Parameters of AutomationEngine['evaluateCondition'] at index 0, which on the head is string | { dialect?: string; source?: string; ast?: unknown } (packages/services/service-automation/src/engine.ts:12225), so the door's accept-set equals the engine's by construction rather than by a copied union. AutomationEngine is exported from @objectstack/service-automation (src/index.ts:4), and that package is already in @objectstack/verify's runtime dependencies, so the emitted dist/index.d.ts resolves for a consumer. The slot typed as the provider class follows the handle's own precedent (ObjectQL for objectql, TenancyService for tenancy). The alias is not exported; a consumer reads it off the handle, as the new test does. Observation, not a defect.
  3. The implementation re-derives nothing. Right. It resolves kernel.getServiceAsync('automation') at call time, the same lookup as the handle's engine() door (handle.ts:422), and returns automation.evaluateCondition(condition, new Map(Object.entries(variables))). No catch, no envelope wrap, no dialect sniff, no caller resolved. Object.entries binds each own enumerable string key, which is what the JSDoc, README and changeset say. The engine's own false arms (a non-predicate dialect, an empty source) and its throw arms (structural refusal, CEL fault, an unbound variable) both pass through unchanged, so "a condition the engine cannot evaluate rejects, never answers false" is an accurate description of the fault arms.
  4. The no-service answer is the kernel's, exactly as documented. Right. pluginLoader.getService throws serviceNotRegisteredError(name) for an unregistered name (packages/core/src/plugin-loader.ts:241): branded, code: 'SERVICE_NOT_REGISTERED', serviceName, message Service 'automation' not found. isServiceNotRegisteredError and SERVICE_NOT_REGISTERED_CODE are published from @objectstack/core (src/index.ts:47). No error code is minted in @objectstack/verify, the error-code ledger in packages/spec is untouched, and check:error-code-provenance ran green inside Lint & Repo Gates. The remedy named (automation: true, or requires: ['automation']) is real on the head: BootOptions.automation (harness.ts:377) and CAPABILITY_PROVIDERS.automation in @objectstack/core (capability-providers.ts:65, from item 1 stage 2).
  5. The six pins assert against the engine and the kernel, not against a copy. Right. Each verdict is compared with the engine called directly, each fault message with the engine's own throw, and the no-service rejection with the kernel asked directly; the spy pin checks the same envelope object reaches the engine. The no-service boot uses a second manifest id, so the one-kernel-per-configuration instance rule is not tripped. Covered by Test Core (success).
  6. Doc surfaces. Right. The handle door roster (handle.ts head comment), the VerifyStack member list (harness.ts:146-151, docblock only) and the README (handle section, example, refusal bullet, API list, the automation boot option) each name the door, and every README sentence checked matches the implementation. No content/docs page enumerates the handle's doors (zero hits on the head outside releases/), so the README is the one doc surface and it is updated. The docs-drift advisory names one release-owned page, untouched, as it must be.
  7. Scope and landing shape. Right. No governed surface among the six paths (Governed Surface Queue Guard success); 302 changed lines, under the 3,000-line human-merge threshold; nothing in packages/spec, packages/services, packages/rest or packages/objectql. The PR body's first line is Part of #22301; no closing keyword precedes a card number in the body or in any of the four commit messages (Part-of PR must not also close its card is success on both triggers), so landing closes nothing — items 5, 6, 7 and item 1's remaining composition gap stay open on the card. The four commits carry the model-free trailer pair.

② Semver level

  • @objectstack/verify — publishes (17.7.0 on npm, publishConfig.access: public). Changeset .changeset/22301-verify-evaluate-condition-door.md: '@objectstack/verify': minor, with its own line Clause-②: yes (widening). Right. A new door on the published handle is a new member on a public payload, so yes, at least minor; (widening) is the one arm that applies, since nothing an author could write before is refused now. The compile-break for a hand-built literal is judged under the repo's standing convention for this very interface (items 2 and 3, PR feat(verify): the handle gains a system-context update door and a predicate update door #22517: hooks.updateWhere and AsSystem landed at minor under Clause-②: yes (widening), PASS 6087960766): createHandle is the one implementation the package supports, and a test double is not an implementer surface. Not breaking, so no migration text or ADR-0087 marker is owed. The body carries no tracker number.
  • packages/qa/dogfood — does not publish (private: true, package.json:4). No changeset owed, none present. Right.
  • Clause-②: line. The PR body carries Clause-②: yes (widening) on a line of its own, equal to the changeset's. The level and the declaration agree; Check Changeset is success. No skip-changeset label is on the PR, and none would be right.

③ Boundary flags

  • open_questions[0] (report 6091573005): keep the kernel's SERVICE_NOT_REGISTERED rejection (A) or mint a verify-owned refusal carrying the remedy (B). The seat answered A (6091663799); this record judges A right on the diff. The handle's engine() door answers an empty objectql slot through the same getServiceAsync, so A keeps one vocabulary and core's published discriminator; B would mint a code and therefore a packages/spec ledger row plus a spec changeset, the exact shape that cost PR feat(verify): the handle gains a system-context update door and a predicate update door #22517 two FAIL rounds, for a remedy sentence that already ships in the JSDoc inside dist/index.d.ts and in the README. Answered: A.
  • harness.ts touched outside the claim's letter (docblock only): the member list would be false after this change; a doc-only edit of the file the member sits on. Answered: right.
  • The delivered refusal is the kernel's error, not a verify-typed refusal with status: the same question as above; the dispatch order is not an input to this record, and on the diff the delivered shape is the right one. Answered: A.
  • Two ablation legs instead of one: more evidence, each restored blob-equal by the report; the check-runs on the head are the gate verdicts this record relies on. No flag.
  • The full verify suite ran at 0aac167d63, not at the head: the two later commits are a docblock re-wrap and the one-line dogfood stub; Test Core, Dogfood Regression Gate and the three Type Check jobs on 1582733139 are success, which supersedes the local reading. Answered.
  • Zone 2 named an api door on the handle; api is on VerifyStack: not material to the diff. Answered.
  • Commit trailers in the model-free pair; container restart mid-run; origin/main moved with no merge taken: the first is the form AGENTS.md requires; the second left no trace in the diff; the third leaves the PR mergeable with no overlap, and the queue rebuilds on current main. No flag.
  • Patch round (report 6091913336): the claim's surface widened to the dogfood literal. Necessary and minimal (+1 line), judged right in ① item 1. The dev owns the root cause (a required member added without typechecking import-side consumers). The tooling half, that dispatch-gates --commands derives no @objectstack/dogfood typecheck family from a producer-only path set even though dogfood consumes verify's types, is escalated to the owning PM seat as an observation on the derivation, not filed by this record; CI's Type Check · workspace is the compensating control and it did its job.
  • The PR body was written once by the dev; the seat patched it: the body on the head carries the dogfood bullet and the patch-round evidence, so what it describes is what the diff contains. Answered.
  • The seat's ACCEPT 6091663799 names 79e9053e50; this record binds to 1582733139: the delta is the one dogfood line, judged above. Answered.
  • Acceptance notes carried, not filed, both right and without reach: the automation slot typed as the engine class (one producer of the slot in packages/, service-automation/src/plugin.ts:975; the handle already types objectql the same way); the route doors (flows.*, the dispatcher's own envelope) and this engine door (the kernel's in-process error) answering an empty slot differently, which is the split the handle documents.
  • Docs-drift advisory: one release-owned page named, untouched; two files without an anchor are the README and the docblock-only harness.ts, both read here directly. No flag.

Implemented-by: claude/issue-22301-evaluate-condition
Reviewed-by: session_01KNKBCRDJCu5tGy3TEbvtrF

VERDICT: PASS


Generated by Claude Code

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 tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants