Skip to content

fix(verify): the dashboard date-window family also proves sums and derived measures, and says why an average stays out (#15953 slice 8) - #24385

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-15953-s8-window-noncount-residue
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-15953-s8-window-noncount-residue

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #15953
Clause-②: no

What this is

Slice 8 of the card is the date-window family's residue: the non-count measures the family measured excluded. For each measure kind the family excluded, this PR decides by measurement whether the window property can be derived honestly from the declaration.

Measure kind (fixture pin before this PR) Verdict Why
sum (no-count-measure alone, not-a-count beside a count) D, implemented A sum is additive. Each fixture row holds the summed field to the value the CRUD derivation synthesizes, so the sum's difference over that value is the number of fixture rows the window took in. The app's own rows cancel in the difference, as they do for a count.
derived ratio / sum / difference / product over counts or sums (closed_rate) D, implemented Measured: the widget's own answer to a derived measure carries its operand columns beside it. Each operand is proven as a count or a sum. The value shown must equal evaluateDerivedMeasures, the server's own evaluator exported by @objectstack/service-analytics, over the operands in that row.
avg (no-count-measure / not-a-count), and min, max, count_distinct stays excluded, with a precise reason Not additive. What a fixture row does to an average depends on how many rows the app already holds in the window, and the average does not reveal that number. A minimum or maximum depends on their extreme, and a distinct count on their values. Deriving it would mean guessing that data. A derived measure over one of these, or over another derived measure, is also excluded and names the operand.

object-undeclared, dataset-undeclared and the unparsed kinds are unchanged.

Measured

On bootStack, with a scratch app (not committed), through POST /analytics/dataset/query as the signed-in admin. These readings decided the design:

  • Empty set, no dimensions: count answers [{"nn":0}] and sum answers [{"effort_sum":0}]. avg answers [{"effort_avg":null}]. A ratio answers [{"nn":0,"closed_n":0,"rate":null}]. With dimensions every kind answers [].

  • A derived measure's answer carries its operands: { measures: ['rate'] } under a window answered [{"nn":2,"closed_n":1,"rate":0.5}]. Grouped by status, each row carries its own operands ({"status":"Open","nn":1,"closed_n":0,"rate":0}). This is the reading that makes a derived measure provable without guessing: the operands are read from the widget's own request, not from a second query.

  • Sums behave as expected: a sum over decimals answered 10.6 under a window and 10.8 without one. An undeclared measure is refused 400 INVALID_FIELD.

  • hotcrm (read-only shallow clone at bb8e207, static read only, not run): the three non-count widgets that the hand-written pin of describe 2 blank-checked are:

    • avg_resolution_time, an avg (stays excluded);
    • sla_compliance_gauge, sla_met_count / closed_count (both counts, now probed);
    • kb_deflection_rate, kb_resolved_count / closed_count (counts, one of them of a lookup field, now probed).

    The opportunity dashboards' bound sums of amount are probed too. Their averages (avg_amount, avg_probability) stay excluded. NOT MEASURED: a hotcrm run. os verify cannot boot hotcrm on this tree until its protocol-18 move; that is the card's open acceptance clause.

How the family reads each kind (packages/verify/src/dashboard-window.ts)

  • Every verdict is now in fixture rows. Each probed measure gets a plan: the additive measures it reads (itself, or a derived measure's operands), with what one fixture row adds to each (1 for a count, the held value for a sum).
  • Held columns: every operand's filter is held jointly, as the family already holds the widget's, dataset's and measure's filters. A count of a field holds a value there. A sum holds a non-zero number. A conflict, or a field with nothing to synthesize, excludes the measure and says why.
  • judge checks that every additive measure took in exactly the rows the window selects. A derived measure must also show the server's evaluator over its operands, row by row. For a count the outcome and detail text are unchanged: held is now checked before the shown === 0 blank, and for a count the two can never both hold.
  • A sum of floats is snapped to a whole row only within its own rounding noise (2^-36 of its magnitude). Where that noise reaches a quarter row, the preset is not-run and the detail says why.
  • No export, option or type moves. The exported types gain doc text only, and the excludedKind values are unchanged.

Controls and one-time ablation

  • Fixture app: 27 widgets. Of 17 probed measures, 11 are counts, 3 sums (one beside a count, one grouped) and 3 derived (a ratio and a difference of counts, and a ratio over a sum). All are proven under 13 presets: 221 windows held. An average, a derived measure over an average, a nested derived measure and a count_distinct are excluded with their reasons.
  • Negative controls: the app plugin that drops the window, and the one that zeroes it, each reach the new hours (sum) and share (ratio of counts) widgets. Drop gives contradicted on 13 of 13 presets and inert. Zero gives blank on 13 of 13.
  • Derived-value control: a door that halves flagged_share while leaving its operands as answered. Every operand holds, and every preset is contradicted, with the detail naming both values.
  • Ablation, at 55c7623b2, through scripts/ablation-replace.mjs in wrap mode, inside a script with an absolute-path trap restore. No build is involved: the test imports the module by a relative path.
    • A. const off = taken.find(...) becomes undefined. The anchor goes 1 to 0, and the blob 3fa6388f becomes e9c25e54. Result: RED, Tests 8 failed | 7 passed (15): both modes' window controls, the sum and derived controls, and the derived-value and attribution summaries. Restored: blob 3fa6388f equals HEAD, and git diff HEAD is empty.
    • B. The derived consistency comparison becomes an unconditional continue. The blob 3fa6388f becomes d5f4ff11. Result: RED, Tests 4 failed | 11 passed (15): the derived-value control in both modes, plus the attribution test, whose summary counts that widget. Restored the same way.
    • Both legs were predicted red and observed red. The fixture app stayed green in both.

Tests (the family at 49c5fd871; patch round 1 at 0300fc4b6)

  • pnpm --filter @objectstack/verify exec vitest run --maxWorkers=2 reads Test Files 42 passed (42) / Tests 368 passed (368). The lock verdict line of that call was not printed, because the typecheck chained after it was cut by the shell timeout while the gate battery shared the box. The same suite at 55c7623b2 (main merged once before) reads 42 / 368 passed, VERDICT command-exit 0.
  • pnpm --filter @objectstack/verify typecheck (re-run alone) reads check:test-typecheck: OK, VERDICT command-exit 0. tsc --listFilesOnly -p tsconfig.test.json lists dashboard-window twice.
  • eslint --no-inline-config --format json over the 2 changed source files: 0 errors, 0 warnings.
  • test:repo was not run locally: the diff touches no repo-project test, so it is declared to CI.
  • Patch round 1 at 0300fc4b6 (docs and the flag description only):
    • pnpm --filter @objectstack/cli typecheck reads check:test-typecheck: OK, VERDICT command-exit 0.
    • The 8 test/verify-*.test.ts files that spawn os verify run in the integration tier (--project integration) and read Test Files 8 passed (8) / Tests 24 passed (24), VERDICT command-exit 0.
    • eslint --no-inline-config over packages/cli/src/commands/verify.ts: 0 errors, 0 warnings.
    • NOT MEASURED: the cli unit tier. Reason: it did not finish inside the 595 s foreground cap on the shared box (41 of 285 files reported, 0 failed). No unit-tier test imports commands/verify, so it is declared to CI (Test Core).

Gates (the family at 49c5fd871; patch round 1 at 0300fc4b6)

  • dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 63 commands. All 63 exit 0. check:dual-build-cjs-loads first answered exit 3 (PREREQUISITE NOT MET), and after turbo run build --filter=!@objectstack/docs (76 tasks, 75 cached) it answered exit 0.
  • --ran reads 63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN.
  • The declared wide-population families whose population covers packages/verify/src/dashboard-window.ts, run locally, all exit 0: check:init-service-contract, check:live-db-isolation, check:optional-error-sink, check:resume-authority-declared, check:route-envelope, check:runner-env-posture, check:settings-bind-window, check:startup-registry-verdict, check:verify-stand-in and check:wildcard-fallthrough.
  • Patch round 1 at 0300fc4b6:
    • dispatch-gates.mjs --commands derives 96 commands over 7 paths: the 63 above, plus 33 docs, i18n and spec-docs families for cli.mdx, the README and verify.ts. All 96 exit 0, and --ran reads 96 derived, 96 run, 0 NOT-MEASURED, 0 UNRUN.
    • The dist-reading gates were re-run after the workspace build, and all exit 0.
    • The same 10 wide-population families cover packages/cli/src/commands/verify.ts, and all exit 0.
    • CI on 49c5fd871 had converged before the push: 34 success, 6 skipped, none red.

Docs and flag text (patch round 1, the claim amended in place)

The open question was answered A. The claim 6252766100 is amended to name these three files, and cli.mdx is declared on #6023. They are fixed in this PR at 0300fc4b6, with the replacement texts this section used to carry:

  1. packages/verify/README.md:
    • the family bullet reads "per bound widget and measure (a count, a sum, or a derived measure over those)";
    • the status table reads "Probes per measure that is a count, a sum or derived over those". The contradicted row adds "or a derived measure showed another value than the server's evaluator makes of its operands". The excluded row reads "no measure it can compare (an avg, min, max or count_distinct is not additive)".
  2. content/docs/deployment/cli.mdx, the os verify section, reads "For each measure of a bound widget that is a count, a sum, or a derived measure over those". The excluded list reads "an average, a minimum or maximum, a distinct count, or a derived measure over one".
  3. packages/cli/src/commands/verify.ts, the --dashboard-windows description, reads "(date and datetime fields; count, sum and derived measures)". It carries a patch changeset on @objectstack/cli (.changeset/15953-s8-cli-dashboard-windows-flag.md, Clause-②: no).

Acceptance notes

  • With hotcrm's hooks live, its own hook derives is_closed from status (slice 5, run B). So the two newly provable ratios hold is_closed: true, which reads back false. When hotcrm runs, they are expected to read inconclusive under live hooks: an app fact, reported, never contradicted.
  • A sum's held value is the CRUD derivation's synthesized one (7 for a number, 100 for a currency, 50 for a percent, clamped to the declared min/max/step). A field declared max: 0 or below would make it non-positive. That case is handled through the rows-equivalent division, and a zero value is excluded.

Generated by Claude Code

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 18, 2026
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/natural-language-queries.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/api/data-api.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/data-modeling/queries.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/deployment/cli.mdx (via os verify (command, read off packages/cli/src/commands/verify.ts))
  • content/docs/deployment/validating-metadata.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/kernel/contracts/data-engine.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/protocol/objectql/query-syntax.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/ui/dashboards.mdx (via count_distinct (literal, a string literal in notAdditive))

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

  • content/docs/releases/v15.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/releases/v17/17-0.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/releases/v17/17-4.mdx (via os verify (command, read off packages/cli/src/commands/verify.ts))
  • content/docs/releases/v17/17-5.mdx (via count_distinct (literal, a string literal in notAdditive))
  • content/docs/releases/v17/17-6.mdx (via count_distinct (literal, a string literal in notAdditive), os verify (command, read off packages/cli/src/commands/verify.ts))
  • content/docs/releases/v17/17-7.mdx (via count_distinct (literal, a string literal in notAdditive), os verify (command, read off packages/cli/src/commands/verify.ts))

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
  • 1 changed file(s) yielded no anchor (packages/verify/README.md) — pages documenting those are invisible to this run
  • 10 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 — 31 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 b2e78701f779809b588096d82f5d86ba4ab5cfde → packageMentionDocs.

Which tree this was computed on

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

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

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — REWORK (patch round 1: the three published texts, answered A)

domain:cli seat 1 (seat post #6024) · zhuangjianguo · session session_01SYEJqDL3NXxrJRBVqaMu9T · 2026-10-18T18:53Z · on head 49c5fd871 · #15953 slice 8 (claim 6252766100, amended in place in this act). This is the seat's own review at the default tier (Clause-②: no), read against GitHub, not the report.

The family change is accepted as it stands:

  • Sums are derived: the held value per fixture row, and the difference divided by it is the rows taken in, snapped only within float noise.
  • Derived measures over count or sum operands are derived. The operands are read from the widget's own answer and each is proven as a count or a sum. The shown value must equal @objectstack/service-analytics's exported evaluateDerivedMeasures, so nothing is re-implemented.
  • avg, min, max and count_distinct stay excluded, each with a reason: they are not additive, and the answer depends on the app's own rows in the window.
  • Controls: drop / zero / derived-value. Two ablation legs went red as predicted. verify passed 42 files / 368 tests. No new export, option or excludedKind value.

Owed in this PR (your open question, answered A): three published texts this slice makes false or incomplete. Fix them here with the replacement texts your PR body already carries:

  1. packages/verify/README.md: the family bullet and the status table.
  2. content/docs/deployment/cli.mdx: the os verify section, which says sums and derived measures are excluded. Declared on [PM seat] domain:devx @ objectstack — 🟢 os-warren · session_01FM29ZqLTrnPNXxijioZwMp #6023 6253787210.
  3. packages/cli/src/commands/verify.ts about :158: the flag description, with a patch changeset on @objectstack/cli, because its dist changes.

The claim is amended in place to name all three. The PR body's "Owed before this lands" section becomes stale once they land. The seat corrects it from your report's exact edits; do not patch the body.

Before pushing: read this head's checks (still converging at the report) and fold any red into the same round.


Generated by Claude Code

…ms and derived measures

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

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT (patch round 1)

domain:cli seat 1 (seat post #6024) · zhuangjianguo · session session_01SYEJqDL3NXxrJRBVqaMu9T · 2026-10-18T19:25Z · on head 0300fc4b6 · #15953 slice 8 (claim 6252766100, as amended; patch-round report 6254104030). This is the seat's own review at the default tier, read against GitHub, not the report. It completes REWORK 6253794414, whose acceptance of the family change stands.

  1. What moved this round: one commit, read on the compare 49c5fd871...0300fc4b6. It touches four files, +22 / −9, and changes no code path or test:
    • packages/verify/README.md: the family bullet and the status table (contradicted adds the derived-measure arm; excluded reads "no measure it can compare (an avg, min, max or count_distinct is not additive)");
    • content/docs/deployment/cli.mdx: the os verify section no longer says sums and derived measures are excluded (declared on [PM seat] domain:devx @ objectstack — 🟢 os-warren · session_01FM29ZqLTrnPNXxijioZwMp #6023 6253787210);
    • packages/cli/src/commands/verify.ts:158: the flag description reads "count, sum and derived measures";
    • .changeset/15953-s8-cli-dashboard-windows-flag.md: @objectstack/cli patch, because the help text ships in its dist. readClause2Line reads its line as no with no arm; Check Changeset is success.
      Each text was checked against dashboard-window.ts at this head. A sum's difference is divided by the value each fixture row holds, so a verdict stays in fixture rows.
  2. The dev's deviation (neighbouring count-first phrases left as they are): accepted. "Each difference must equal the fixture rows the window selects" and "must count every fixture row first" stay true, for the reason in item 1. None of them says a sum or a derived measure is excluded. → Acceptance notes.
  3. PR body: its Tests, Gates and "Owed before this lands" sections were stale after the push. The seat corrected them with the report's five exact edits, each matching once. It was written through the relay and read back identical (11,482 bytes).
  4. Shape:
    • Part of #15953, Clause-②: no. 7 files, +516 / −133 (649 changed lines). Not governed, under 3,000.
    • Two changesets: @objectstack/verify (the family) and @objectstack/cli (the help text).
  5. Tests and gates, from the report:
    • The 8 os verify integration files pass, 24 tests. cli typecheck exits 0. The cli unit tier is declared to CI: no unit test imports commands/verify.
    • dispatch-gates: 96 derived, 96 run, 0 NOT-MEASURED. The 10 wide families cover verify.ts and exit 0.
    • CI on this head: lint, type-check and changeset jobs are success; Test Core and Build Docs were running at this stamp.
  6. Landing: ready plus auto-merge through the queue once every check on this head is green or an expected skip. After the merge, verify: classify hotcrm's "platform-semantics pins" — each becomes a derived proof family in @objectstack/verify or a platform regression test in dogfood, never an app test (epic hotcrm#1579, step 5c) #15953 returns to pm:queue with a Release: line. Its acceptance still waits on hotcrm's protocol-18 move.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants