Skip to content

docs(spec): the broken-sweep predicate in FlowFunctionEffectSchema is a first FILTER, not the detector - #13068

Merged
os-trump merged 1 commit into
mainfrom
claude/issue-12900-flow-function-broken-sweep
Aug 29, 2026
Merged

docs(spec): the broken-sweep predicate in FlowFunctionEffectSchema is a first FILTER, not the detector#13068
os-trump merged 1 commit into
mainfrom
claude/issue-12900-flow-function-broken-sweep

Conversation

@os-trump

Copy link
Copy Markdown
Collaborator

Fixes#12900

The third packages/spec surface carrying the unqualified broken-sweep claim. #12685 measured the A/B on one graph pair through the real engine (pinned in run-summary.test.ts): a healthy idempotent sweep — re-select the same records, gate each one on "already handled" — and a dead gate BOTH report selected > 0, acted 0, unmeasured 0. "Over N consecutive runs" does not rescue it: the healthy steady state is persistent for as long as the outstanding work stands, so it trips on every run; consecutiveness filters flapping, which is a different failure.

TSDoc and generated-docs prose only — no behaviour, no schema, no accept-set change. No ablation is owed here: nothing executable changed, so there is no guard whose removal could be shown to turn a check red. The measurement that backs the prose already has its executable twin in run-summary.test.ts.

Per-mention triage

Five mentions in packages/spec/src/automation/flow-function.zod.ts, judged individually rather than swept.

#where (base 9e8adf3f6)beforeafterverdict
1@module block, ~:38-42 — emitted verbatim on the docs page"it would suppress the broken-sweep signal on every flow that calls any function""it would drop every flow that calls any function out of the broken-sweep FIRST FILTER (selected > 0 AND acted = 0 AND unmeasured = 0) … That predicate is a first filter and not a verdict (#12685: a healthy idempotent sweep … matches it on every run too, and what discriminates is the per-node fold in FlowRunSummary.nodes[] / gates[])"reworded — see the note below
2FlowFunctionEffectSchema doc block, ~:62-67 — the card's primary"the run's unmeasured tally keeps the broken-sweep query (selected > 0 AND acted = 0 AND unmeasured = 0) off it""keeps the broken-sweep FIRST FILTER (…) off it … The third clause earns its place for the reason it always did: such a run has an INCOMPLETE acted count, not a zero one." plus a new paragraph: "A filter, not a verdict (#12685) … What separates them is the per-node fold on FlowRunSummary (nodes[] / gates[]) … Declaring 'writes' decides which runs enter that first filter; it does not make the filter a detector."reworded — named the three-clause predicate as the detector, definite article, which is exactly what #12685 disproved
3~:70"an under-reported selected can only make the broken-sweep alert quieter, never wrong""…can only make the broken-sweep filter quieter, never wrong"reworded, minimally — the sentence's own claim is true and survives untouched; only "alert" goes, because it is the word that makes the predicate an alarm. This is the same edit #12721 made to unmeasured_count's description
4~:123"so #4354's broken-sweep query stays silent on the one flow that needed it""so #4354's broken-sweep filter stays silent on the one flow that needed it"reworded, minimally — same reason; the surrounding claim about a misspelled effect key is untouched
5the authored history string, ~:155"which is what keeps the broken-sweep alert quiet on the run that needed it.""which is what keeps the broken-sweep filter quiet on the run that needed it."reworded, minimally — authored schema text, so treated with schema-text care: one word, no restructuring. It reaches an author only as strictObject error-message prose (it is in no generated artifact; grep over the tree finds it in no .json / .mdx / .md outside CHANGELOG.md), and no test pins its content — flow-function.test.ts asserts on the surface name and the `efect` → `effect` prescription, both untouched

Left standing, deliberately: ~:23 — "which reads exactly like the broken sweep #4354 exists to detect". This claims nothing about the three-clause predicate. It says an under-reporting run resembles a broken sweep, which is true before and after #12685, and is in fact the premise the rest of the module rests on. Rewording it would be a change for uniformity's sake with no defect behind it.

Note on mention 1, which the card's file surface did not enumerate

The card measured that this file's @module doc comment is emitted verbatim as the intro of content/docs/references/automation/flow-function.mdx, and required the regeneration on that basis. Re-measuring on the base showed the enumerated four mentions are not the ones that reach that page: the FlowFunctionEffectSchema doc block contributes only its .describe() string to the generated page (under the FlowFunctionEffect heading), and mentions 3-5 do not appear there at all. The prose that reaches the page is the @module block — and it carries a fifth mention of the same claim, in the same defect class, at ~:38-42. "the broken-sweep signal" is the exact phrase #12721 replaced on sys_automation_run.acted_count.

So mention 1 is both the sentence an operator actually reads on the docs page and the only reason the generated artifact moves in this PR. Rewording the enumerated four alone would have produced a PR whose check:generated is green trivially — the #12722 reading — rather than green with the regenerated artifact in the diff, which is what this card was dispatched to demonstrate. It is inside the card's declared file, the same defect class, mechanically corrected by evidence already landed, and no other open PR claims the file; it is named here and quoted above rather than folded in silently.

Cross-surface agreement

These sentences now say the same thing as their siblings:

Generated artifact

content/docs/references/automation/flow-function.mdx is regenerated, never hand-edited:

pnpm --filter @objectstack/spec gen:schema && pnpm --filter @objectstack/spec gen:docs
-> "Successfully generated 1616 schemas." / "Generated 231 files"

The only artifact that moved is flow-function.mdx, mirroring mention 1 exactly (12 lines replacing 5). authorable-surface.base.json was not rewritten by this run; gen:schema printed the expected anchor note ("expected, and not an error: the anchor is a snapshot of an upstream commit, proved AUTHENTIC rather than current") and check:authorable-surface is green.

Evidence

All readings taken on the final commit — git rev-parse --short HEAD = 4d5d782bd, the pushed head, working tree clean. Exit codes captured before any pipe (each gate run redirected to its own log first, then EXIT=$?).

The card-distinguishing readingcheck:generated green with the regenerated artifact in the diff:

pnpm --filter @objectstack/spec run check:generated EXIT=0
"✓ check:docs content/docs/references/**"
"✓ check:authorable-surface authorable-surface/ + authorable-defaults/ (+ its .base.json anchor) + JSON schemas"
"✓ check:api-surface api-surface/"

The first run of that gate exited 1 on a PREREQUISITE, not a finding: "packages/spec/dist holds no .d.ts declarations — the package is not built". pnpm --filter @objectstack/spec build (exit 0, "check-dts-emitted: @objectstack/spec - 34/34 declared declaration file(s) present.") and the re-run above is the measured one. The build is also the proof these blocks ship: packages/spec/dist/flow-function.zod-DoP4q83A.d.ts carries "A filter, not a verdict (#12685)" — read out of a dist rebuilt after the edit, not a cached one.

Type checking, with coverage proven rather than assumed:

pnpm --filter @objectstack/spec typecheck EXIT=0
pnpm exec tsc --noEmit --listFiles EXIT=0
-> packages/spec/src/automation/flow-function.zod.ts is file #242 in the program

(typecheck here is tsc --noEmit && check:scripts-typecheck && check:test-typecheck; the test-layer program is the tsconfig.test.json sibling and it is green too.)

Tests — the package's own suite, and the file that pins the strictObject messages the edited history string feeds:

pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
-> Test Files 441 passed (441) · Tests 11691 passed (11691)
pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/automation/flow-function.test.ts
-> Test Files 1 passed (1) · Tests 23 passed (23)

Gate family — derived from the real change set rather than recalled: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which reported "change set derived from git — 3 path(s) vs merge base 9e8adf3" and 49 matched families. All 49 were run; 47 green, 2 NOT MEASURED for container reasons (both declared below). Among the green ones, beyond the families the dispatch named: check:docs, check:authorable-surface, check:doc-anchors, check:doc-authoring, check:doc-formula-expressions, check:doc-security-posture, check:docs-audit-scope, check:docs-redirects, check:docs-single-h1, check:empty-state, check:liveness, check:merge-driver, check:skill-examples, check:skill-refs, check:spec-parsed-alias, check:strictness-ledger, check:variant-docs, check:quick-reference-counts, check:cross-package-test-inputs, check:test-source-alias, check:type-source-resolution, check:published-files, check:keyed-text-bounds, check:doc-frontmatter, check:docs-section-name, check:section-landing-index, check:changeset-gate-self-tests, check:empty-changeset, check:changeset-no-major, check:adr-0087-registration, check:objectui-changeset, check:pm-half-states, and the docs-drift pair.

Three gates exited non-zero on a PREREQUISITE the first time and were re-run after the prerequisite was met, all green: check:generated (above), check:doc-formula-expressions / check:doc-security-posture ("Nothing was measured: this gate exited before running a single check" — needed @objectstack/formula and @objectstack/lint built) and check:skill-examples (needed @objectstack/client-react built).

Two gates are NOT MEASURED — declared as such, not reported as green and not reported as red. Both are container facts and neither can be moved by this diff:

  • node scripts/check-dev-prereqs.mjs exits 1 in this fresh worktree with "34 of 67 workspace packages declare an entry point under dist/ that is not on disk … Fix: pnpm build". Its population, read from its own output, is declared package entry points versus the filesystem — a build-state fact about the container, not about file contents. This diff contains three files (git diff --name-only), none of them a package.json, and it adds no package, no exports and no main; prose inside a .ts doc comment and a regenerated .mdx cannot move that verdict in either direction. Satisfying it locally means building all 67 packages, which is CI's run — and CI builds before lint, so it reports the real reading there.
  • node scripts/pm/check-half-states.mjs (the half-state-patrol.yml board sweep, as distinct from the lint.yml family pnpm check:pm-half-states = its --self-test, which ran green: "check-half-states self-test: 1551 cases pass.") exits 3 with its own "PREREQUISITE NOT MET — the token in the environment is not a valid GitHub credential … Nothing was swept: no issue was listed, no predicate (H1-H16) ran … It is not a clean board and it is not a dirty one — it is no reading at all." It sweeps the issue board, not the tree, so no file in this diff is in its population.

Byte discipline:

pnpm check:nul-bytes EXIT=0
grep -naP over the three changed files for C0/C1 control bytes -> no hits

Repo-wide lint, not narrowed:

pnpm lint (= eslint . --no-inline-config) EXIT=0

Out of scope, filed

#13063 — three further broken-sweep mentions in packages/spec/src/automation that sit outside the fences of #12722 and of this card: execution.zod.ts ~:57-61 (a .d.ts-shipping block in the same file #12722 corrected elsewhere, still "fires the broken-sweep alert" / "makes the alert never fire"), and test comments in execution.test.ts ~:210 and flow-function.test.ts ~:192-194. That card also records a separate question the reading raised — whether the "stays silent on the one flow that needed it" sentences state their causal direction backwards — which is not the predicate-as-detector defect and is deliberately left unresolved here: the minimal rewords in mentions 4 and 5 leave each sentence's own claim exactly as it was, whichever reading turns out to be intended. That card is open and is not addressed by this PR.


Generated by Claude Code

… a first FILTER
`packages/spec/src/automation/flow-function.zod.ts` stated
`selected > 0 AND acted = 0 AND unmeasured = 0` as *the* broken-sweep query,
unqualified -- the third `packages/spec` surface carrying the claim, outside
the fence of the change that corrected `automation/execution.zod.ts` and
`integration/connector.zod.ts`.
#12685 measured the A/B on one graph pair through the real engine: a healthy
idempotent sweep -- re-select the same records, gate each one on "already
handled" -- and a dead gate BOTH report `selected > 0, acted 0, unmeasured 0`.
"Over N consecutive runs" does not rescue it: the healthy steady state is
persistent for as long as the outstanding work stands, so it trips on every
run; consecutiveness filters flapping, which is a different failure.
Reworded to the shape the sibling surfaces now agree on: the predicate is the
FIRST FILTER, not a verdict, and the per-node fold (`FlowRunSummary.nodes[]` /
`gates[]`) is the discriminator. Each clause keeps its own true point -- a
declared-`writes` function makes `acted` INCOMPLETE rather than zero, and an
under-reported `selected` can still only make the filter quieter, never wrong.
This file's `@module` block is emitted verbatim as the intro of
`content/docs/references/automation/flow-function.mdx`, so the artifact is
regenerated here with `gen:schema && gen:docs` -- never hand-edited.
TSDoc and generated-docs prose only -- no behaviour, no schema, no accept-set
change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tooling labels Aug 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 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.

Coarse fallback — 126 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 6c6157af05b25b28be0b3a7e92ce03ef85a98653packageMentionDocs.

Which tree this was computed on

This run read content/docs from 49f8025c725e6e5dbb2cc8e4390e94403a92146b — the merge of head 4d5d782bd8e4e39b1e5ad3789b963b493a630f2d into base 6c6157af05b25b28be0b3a7e92ce03ef85a98653, 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 49f8025c725e6e5dbb2cc8e4390e94403a92146b && git checkout 49f8025c725e6e5dbb2cc8e4390e94403a92146b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6c6157af05b25b28be0b3a7e92ce03ef85a98653 4d5d782bd8e4e39b1e5ad3789b963b493a630f2d && git checkout -B drift-repro 6c6157af05b25b28be0b3a7e92ce03ef85a98653 && git merge --no-ff 4d5d782bd8e4e39b1e5ad3789b963b493a630f2d
node scripts/docs-audit/affected-docs.mjs --json 6c6157af05b25b28be0b3a7e92ce03ef85a98653

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

@os-trump
os-trump marked this pull request as ready for review August 29, 2026 02:48
@os-trump
os-trump enabled auto-merge August 29, 2026 02:48
@os-trump
os-trump added this pull request to the merge queueAug 29, 2026
Merged via the queue into main with commit eeba2efAug 29, 2026
35 checks passed
@os-trump
os-trump deleted the claude/issue-12900-flow-function-broken-sweep branch August 29, 2026 03:12
os-trump pushed a commit that referenced this pull request Aug 29, 2026
…rst FILTER, and the misspelled-effect sentences state the measured direction
#12685 measured that `selected > 0 AND acted = 0 AND unmeasured = 0` cannot
separate a healthy idempotent sweep from a dead gate. #12721, #12722, #12900 and
#13068 each rewrote the surfaces that stated it as a detector, and each was
fenced to the doc blocks its dispatch named -- so three mentions inside
`packages/spec/src/automation` were left standing rather than corrected in
passing. One of them ships in `.d.ts` and is what a platform author reads.
Reworded to the shape the sibling surfaces now agree on: the predicate is the
FIRST FILTER and not a verdict, the per-node fold (`FlowRunSummary.nodes[]` /
`gates[]`) is the discriminator, and each clause keeps its own true point.
1. `execution.zod.ts` -- the `unmeasuredEffect` rationale no longer says an
understated `0` "fires the broken-sweep alert on a healthy run until
operators learn to ignore it". That muting is not peculiar to an understated
`0`: after #12685 the filter selects every healthy idempotent sweep. The
block now states what a fabricated count really costs -- an understated `0`
puts a run that DID act inside the filter, an overstated `1` keeps a run that
acted on nothing outside it, and a faked `acted` is a fact the per-node fold
can only repeat rather than settle.
2. `execution.test.ts` -- the comment no longer says "the broken-sweep query has
to be able to tell". The assertion under it is unchanged and still correct:
it pins that `unmeasured` is carried distinctly from `acted`.
3. `flow-function.test.ts` -- the comment mirroring the source sentence #13068
reworded now matches it again.
CAUSAL DIRECTION, measured rather than ruled. The card's second half asked
whether the "stays silent on the one flow that needed it" sentences state their
direction backwards. They do. Read forward: a lost `effect: 'writes'`
declaration means the `script` executor reports no `unmeasuredEffect`
(`screen-nodes.ts`: `const unmeasured = registration.effect === 'writes'`), so
the run folds to `selected > 0, acted 0, unmeasured 0` -- which SATISFIES the
three-clause filter. The run lands INSIDE the candidate set reading exactly like
a dead sweep, rather than escaping it. The same file's `@module` block already
said so ("which reads exactly like the broken sweep #4354 exists to detect"),
and `packages/qa/dogfood/test/flow-function-effect.dogfood.test.ts` asserts both
legs end to end: declared writer -> `unmeasured: 1` (filter does not match),
pure -> `unmeasured: 0` with the filter "free to fire on this run".
The card's alternative reading -- a flow whose only work is inside the function,
so `selected` stays 0 -- is disposed of by the same measurement: there the
filter is quiet with OR without the declaration, which makes the sentence
vacuous rather than correct.
Bounded in-place: `flow-function.zod.ts` is one file outside the three-mention
surface, carrying the two source sentences that mention 3 mirrors. Fixing the
mirror while leaving its source backwards would manufacture exactly the
disagreement this card exists to remove, so both are corrected here -- the
`FlowFunctionDeclarationSchema` TSDoc and the author-facing `history` string in
its unknown-key message.
Prose and one error-message string only -- no behaviour, no schema, no
accept-set change. `check:generated` reports all 14 artifacts up to date, so
nothing regenerated.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mtooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlowFunctionEffectSchema TSDoc still states the broken-sweep predicate unqualified — the third packages/spec surface, outside #12722's fence

2 participants

@os-trump@claude