Skip to content

fix(driver-memory): a cube $lte on a bare day keeps the whole day on a declared datetime field (#20661) - #20714

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-20661-memory-analytics-whole-day-first
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-20661-memory-analytics-whole-day-first

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #20661

Clause-②: no

The memory analytics (cube) face now applies the whole-day rule for a bare-day $lte BEFORE it converts the bound to the field's storage form, as ADR-0053 D-E3 orders. On a declared datetime field, where { created_at: { $lte: '2026-07-28' } } now answers the same rows as find(), and the SQL echo shows the bound that runs.

The change

packages/drivers/driver-memory/src/memory-analytics.ts:

  • New module function lteUpperBound(authored, comparand, storageForm). It asks nextUtcCalendarDay about the AUTHORED value and returns one of three decisions: before (a bare day: the next day's midnight, converted to storage form, exclusive), unbounded (9999-12-31, which has no next day), or through (anything else: the authored comparand in storage form, inclusive).
  • Both lte rows, the mingo $match row (CUBE_OPERATOR_TO_MONGO_PREDICATE) and the SQL echo row (CUBE_OPERATOR_TO_SQL_PREDICATE), now only render that one decision. They used to call nextUtcCalendarDay(comparands[0]), and comparands is already in storage form. On a declared datetime field that value is the instant 2026-07-28T00:00:00.000Z, which the helper correctly refuses to widen.
  • The storage-form conversion still has one home. comparandsFor became storageFormFor(cube, member), which returns the per-member conversion (still driver.filterComparandStorageForm). Each exit maps its comparands through it and passes the same function to its builder as storageForm, for the one bound a builder derives instead of receiving. comparands is still computed for every operator, so every conversion (and any refusal it raises) happens exactly as before.
  • nextUtcCalendarDay (packages/spec calendar-day.ts) is untouched. The driver's own filter path (memory-driver.ts) was already in this order (nextUtcCalendarDay(val), then store(nextDay)) and is untouched.

The landing site is the one the claim expected. The producer is the emitter, not filterComparandStorageForm: ADR-0053 D-D1 keeps the storage-form conversion operator-blind and puts the operator-sensitive rewrite on the emitters.

Before and after, measured at the published export

Built @objectstack/driver-memory dist/index.mjs (MemoryAnalyticsService, InMemoryDriver). Before = origin/main at fa0a4b6610 (this branch's base; e4e5222b7 plus one commit that does not touch this package). After = this branch. Rows r28 created_at: 2026-07-28T10:00:00.000Z and r27 2026-07-27T10:00:00.000Z. "Declared" means syncSchema with created_at: datetime and made_on: date.

field where find() cube query() before echo before cube query() after echo after
created_at undeclared $lte '2026-07-28' r27, r28 r27, r28 created_at < '2026-07-29' r27, r28 unchanged
created_at declared datetime $lte '2026-07-28' r27, r28 r27 created_at <= '2026-07-28T00:00:00.000Z' r27, r28 created_at < '2026-07-29T00:00:00.000Z'
made_on declared date (control) $lte '2026-07-28' r27, r28 r27, r28 made_on < '2026-07-29' r27, r28 unchanged
created_at declared datetime $lte a Date at 2026-07-28T00:00Z (control) r27 r27 created_at <= '2026-07-28T00:00:00.000Z' r27 unchanged
created_at declared datetime $lte '9999-12-31', rows c26 2026-07-15T14:00Z, mid 9999-12-31T10:00Z, none c26, mid, none c26, none created_at <= '9999-12-31T00:00:00.000Z' c26, mid, none created_at IS NOT NULL
created_at undeclared $lte '9999-12-31', same rows c26, mid, none c26, mid, none created_at IS NOT NULL same unchanged

In the last-day rows, none was written with created_at: null, and the driver's create stamps its created_at default in its place, so that row has a value. The pinned test uses no such row.

Dispatch hypotheses

  • H1, held. At fa0a4b6610, both lte rows called nextUtcCalendarDay(comparands[0]) (:273, :468). comparands came from comparandsFor (:1555, called at :886 and :1367), and the builder context also carried raw. The fix widens from raw[0] and converts only the widened bound, through the same conversion the comparands take. It is one function that both rows render, so the two exits cannot drift.
  • H2, held. $between is not in MONGO_TO_CUBE_OPERATOR. Measured on both exits with a declared datetime field and a bare-day maximum: INVALID_FILTER / 400 ("declared by the Filter Protocol but cannot be compiled by driver-memory's analytics (cube) face"). No row added. The new test pins that refusal on the date shape, so widening the table to take $between turns it red and points at lteUpperBound.
  • H3, held. A timeDimensions[].dateRange end widens the raw end string before it builds the string and Date bounds (:1005), so it already follows D-E3. On a declared datetime field, ['2026-07-01', '2026-07-28'] answers r27, r28 before and after, the same as find() with $gte / $lte. Pinned, not changed.
  • H4, held. On a declared date field the storage form of a bare day is that bare day, and the answer and echo are byte-identical before and after (table above). Pinned.
  • H5, held. fix(spec,drivers): a datetime $lte or $between maximum on 9999-12-31 includes the whole last supported day (#20600) #20643's isUnboundedAbove path still answers "has a value". Its existing pins in memory-driver-calendar-day-upper-bound.test.ts (undeclared field) are green. On a DECLARED datetime field the last day was also broken by this card's order (<= '9999-12-31T00:00:00.000Z', dropping mid). It now answers IS NOT NULL / $ne: null and is pinned.

Verification

Tests: new packages/drivers/driver-memory/src/memory-analytics-20661-lte-whole-day-first.test.ts, 7 cases. Every case compares the cube's rows with find() and with a literal id list, and checks the echo's WHERE:

  • the card's pair, undeclared and declared datetime;

  • a full-timestamp $lte (stays inclusive, in storage form);

  • the declared date control;

  • the declared-datetime last day and the day before;

  • the dateRange sibling;

  • the $between refusal on both exits, asserting the envelope code and status.

  • pnpm --filter @objectstack/driver-memory test: 63 files, 1456 tests passed.

  • pnpm --filter @objectstack/driver-memory typecheck (tsc --noEmit && tsc --noEmit -p tsconfig.typecheck.json): exit 0. --listFiles on the first program lists the new test file.

Reverse verification. The fix was committed first (HEAD cb155edf6c). The subject is loaded from src through the test's relative import, so no dist is involved. The mutation went through scripts/ablation-replace.mjs under a trap restoring HEAD: the anchor nextUtcCalendarDay(authored) was replaced with nextUtcCalendarDay(comparand), which is the old order. On disk the anchor went 1 to 0, the replacement 0 to 1, and the blob 014497594cc6 became a41c37fac411. Run over the new file and memory-driver-calendar-day-upper-bound.test.ts: 2 failed, 15 passed. The two failures were the declared-datetime pair (expected [ 'r27' ] to deeply equal [ 'r27', 'r28' ]) and the declared last day (expected [ 'c26', 'prev' ] …). The undeclared, date, instant, dateRange, $between and #20600 cases stayed green, which is the expected direction. Restore: the blob equals the HEAD blob 014497594cc6 and git diff HEAD is empty.

Gates, at 8cae2ae398:

  • node scripts/pm/dispatch-gates.mjs --commands: 60 commands, all exit 0. check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt first exited 3 (PREREQUISITE NOT MET, no built closure). They were re-run green after pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' (71 of 71 tasks).
  • --ran reconciliation: "60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN".

Lint, narrowed and not the repo-wide run:

  • eslint --no-inline-config --format json over the 2 touched .ts files: 2 files, 0 errors, 0 warnings.
  • Both files are in eslint's own population: --print-config resolves a config for each, with 6 and 5 rules.
  • eslint.config.mjs enables no type-aware linting (no parserOptions.project; its own header says so at :327), so this diff cannot move the verdict for any untouched file.

The Clause-②: no line above is the claim's.

Acceptance notes

  • Reach unchanged. No in-repo door constructs MemoryAnalyticsService: git grep 'new MemoryAnalyticsService(' finds 0 non-test hits against 31 in test files (the control). service-analytics plugin.ts:245 mentions it in a comment as a registered fallback "from dev-plugin", but no code in this repo registers it.
  • No other analytics or preview face has this order, by reading.
    • service-analytics native-sql-strategy.ts:1244 widens values[0] and then coerces nextDay.
    • objectql-strategy.ts:548 widens the raw $lte bound.
    • The preview evaluator lteBound is type-blind and converts nothing.
    • driver-mongodb mongodb-filter.ts:1165 widens value and then stores nextDay.
    • None of these was measured at runtime.
  • Behind origin/main by 2 commits. They were 9b384f63ae (service-storage docs) and 5757463712 (platform-objects translations), with no path in driver-memory, spec or core. The branch was not merged forward; CI's merge ref covers them.

Generated by Claude Code

…ore storage form (#20661)

The mingo and SQL-echo lte rows asked nextUtcCalendarDay about the
comparand already in storage form, which on a declared datetime field is
the midnight instant the helper refuses to widen. Both rows now render one
lteUpperBound decision taken from the authored value, and only the
widened bound is converted (ADR-0053 D-E3).

Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY
Co-authored-by: Claude <noreply@anthropic.com>
…nd declared datetime (#20661)

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-memory, touching 12 documentable anchor(s).

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

  • content/docs/releases/v14.mdx (via generateSql (symbol, a method of class MemoryAnalyticsService))
  • content/docs/releases/v17/17-5.mdx (via generateSql (symbol, a method of class MemoryAnalyticsService))

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 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 — 9 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 57574637129bebb4a6868c465be7e1b80eed1954 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 199fc9bab269f329a34294f9f11a6a1fb664700d — the merge of head 8cae2ae3980972016b27ab33b3c6f59cf3197f8e into base 57574637129bebb4a6868c465be7e1b80eed1954, 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 199fc9bab269f329a34294f9f11a6a1fb664700d && git checkout 199fc9bab269f329a34294f9f11a6a1fb664700d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 57574637129bebb4a6868c465be7e1b80eed1954 8cae2ae3980972016b27ab33b3c6f59cf3197f8e && git checkout -B drift-repro 57574637129bebb4a6868c465be7e1b80eed1954 && git merge --no-ff 8cae2ae3980972016b27ab33b3c6f59cf3197f8e

node scripts/docs-audit/affected-docs.mjs --json 57574637129bebb4a6868c465be7e1b80eed1954

⚠️ 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 57574637129bebb4a6868c465be7e1b80eed1954 → 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

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 8cae2ae3980972016b27ab33b3c6f59cf3197f8e
Local-runs: none

Inputs: card #20661 (body; triage 5893953164; claim 5897541483; os-dev-report 5898224404), PR #20714 (body, file list, net diff against origin/main 5757463712 from merge-base fa0a4b6610), and the check-runs on the head. Read-only throughout: git diff origin/main...refs/review/20714, git show origin/main:PATH, REST reads. Governing text: ADR-0053 D-D1 and D-E3 on origin/main.

① Derived judgments

Files: .changeset/20661-memory-analytics-lte-whole-day-first.md (+13), packages/drivers/driver-memory/src/memory-analytics-20661-lte-whole-day-first.test.ts (+165), packages/drivers/driver-memory/src/memory-analytics.ts (+93 / -19). No governed surface on the file list (the Governed Surface Queue Guard check agrees).

  1. Declared-datetime bare-day $lte, both exits — RIGHT. On origin/main the mingo row (:273) and the SQL-echo row (:468) asked nextUtcCalendarDay(comparands[0]), and comparands had already been through filterComparandStorageForm (comparandsFor :1555, called at :886 and :1367), which on a datetime field turns '2026-07-28' into the instant '2026-07-28T00:00:00.000Z' (core temporal-storage-form.ts, instantMs bare-day arm). nextUtcCalendarDay returns null for anything but a bare YYYY-MM-DD string (spec calendar-day.ts:95-98), so the bound stayed inclusive at that midnight. The diff's lteUpperBound(authored, comparand, storageForm) asks the helper about raw[0] (the authored value) and converts only the widened nextDay through the same per-member storageForm: mingo $lt and an echo strict less-than at '2026-07-29T00:00:00.000Z'. That is D-E3's sentence ("runs on the bare-day STRING first; only the resulting bound is converted to the storage form") and D-D1's placement (the rewrite on the emitter, the conversion operator-blind: filterComparandStorageForm is untouched). It is also find()'s order: memory-driver.ts:1745-1747 widens val, then store(nextDay). One decision, rendered by both rows, so the chart and its echo cannot disagree on it.

  2. Declared-datetime $lte: '9999-12-31' — RIGHT. Before: the instant '9999-12-31T00:00:00.000Z' went to the helper, null came back, and the bound was inclusive at that midnight, dropping the rest of the last supported day. After: nextUtcCalendarDay('9999-12-31') is UNBOUNDED_ABOVE (calendar-day.ts:112), so the unbounded arm renders $ne: null / IS NOT NULL, which is what find() lowers at memory-driver.ts:1746 and what the undeclared field already answered ([finding] nextUtcCalendarDay('9999-12-31') answers '10000-01-01', so on SQLite a datetime $lte '9999-12-31' or a $between maximum on that day answers no rows #20600). Same defect, same order, inside the card's scope ("whatever the day"); pinned in the new test with the day before as the bounded control.

  3. Claimed-unchanged inputs — RIGHT, each derived from the code rather than from the PR's table: (a) a full timestamp string or a Date: the helper returns null for the authored value and for its storage form alike, so before and after both render $lte on the comparand in storage form; (b) an undeclared field: coerceTemporalValue(v, undefined) is identity (memory-temporal.ts:51), so raw[0] equals comparands[0] and storageForm(nextDay) is nextDay — byte-identical $lt '2026-07-29'; (c) a declared date field: the storage form of a valid bare day is that bare day, so the widened bound converts to itself — byte-identical. The new test pins (a), (b) and (c) against find() and against a literal id list.

  4. Other cube operators — RIGHT, unchanged. At both call sites comparands is now filter.values.map(storageForm), with storageForm the same closure comparandsFor mapped through (extractTableName(cube.sql), resolveFieldPath(cube, member), driver.filterComparandStorageForm); the map is still eager for every operator, so every conversion and any refusal it raises happen exactly as before. Only the two lte rows read the new storageForm input; the other eleven rows read comparands / raw as they did. The one added conversion is storageForm(nextDay) on the before arm, where nextDay is always a valid four-digit-year bare day; none of the three canonicalisers throws on that input.

  5. One consequence the prose does not name, judged RIGHT: on a declared time field a bare-day $lte now lowers to $lt '00:00:00' where it used to lower to $lte '00:00:00', because canonicalTimeOfDay reads a bare day as that day's midnight. A calendar day against a wall clock is a nonsense comparison either way, and after the diff it is exactly what find() lowers for the same input (memory-driver.ts:1747), which is the card's invariant. No shipped sentence claims time unchanged.

  6. Public surface — RIGHT, none. LteUpperBound, lteUpperBound, MongoPredicateInput.storageForm and SqlPredicateInput.storageForm are module-private. comparandsFor to storageFormFor is a private method rename on the exported class; nothing outside the file names either (0 hits on origin/main and on the head), and no api-surface baseline or ADR anchor lists them. Accept-set: MONGO_TO_CUBE_OPERATOR is unchanged, so no operator was added or removed; $between is still absent and still refuses through uncompilableFieldOperatorError to unsupportedFilterError (code INVALID_FILTER, status 400, filter-refusal.ts:61-66).

  7. Siblings, per triage — RIGHT. dateRange end: origin/main :1005 widens end = String(range[1]) before it builds the string and Date bounds, and that path converts nothing to storage form (the bounds are raw strings and Dates under $or), so it already followed D-E3; pinned against find() with $gte / $lte, not changed. $between: unreachable on this face; the refusal is pinned on both exits with the date shape, so widening the operator map later turns that pin red at the right spot.

  8. Shipped doc comments — TRUE on each claim the diff makes: both rows "used to ask about comparands[0], which is already in storage form" (:273, :468); find() "widens val and converts nextDay" (:1745-1747); "on an undeclared field the storage form is the authored string" (memory-temporal.ts:51); 9999-12-31 "has no next day" (calendar-day.ts:106-112); the two {@link storageFormFor} sites (:157, :697) follow the rename, no stale link.

  9. Test — RIGHT against the pin spec: the undeclared / declared-datetime pair, each asserted equal to find() and to a literal list, plus the echo WHERE; one pin per sibling found (dateRange; the $between refusal envelope on both exits); the last day and the day before; the instant and date controls. The file name follows the package's existing memory-NNNNN-slug.test.ts convention.

② Semver level

@objectstack/driver-memory is released (17.5.0, not private). The diff changes the answer of an input the face already compiled and removes or renames nothing an author or consumer can name, so it is a bug fix in a released package: patch, never skip-changeset. The changeset declares '@objectstack/driver-memory': patch — RIGHT. Check Changeset: success.

Clause-②: no in the changeset and in the PR body — RIGHT. No accept-set widens (the input compiled before; only its rows and echo change) and none narrows (no operator, export, key or field is removed), so no arm is due.

Shipped prose, sentence by sentence against the diff and origin/main:

  • Changeset: every sentence TRUE — the storage form of '2026-07-28' on a datetime field is '2026-07-28T00:00:00.000Z'; the whole-day rule does not widen an instant; generateSql() echoed the same narrowed bound; the last day now asks only for a value as the undeclared field already did; the "Unchanged" list (undeclared, declared date, full timestamp or Date, dateRange end) holds by the derivations in ①3 and ①7; $between stays refused INVALID_FILTER / 400; nothing is removed or renamed on any public surface.
  • PR body (the squash message): the line citations :273, :468, :886, :1367, :1555, :1005 resolve on origin/main to the lines named; the base fa0a4b6610 is the merge-base and is e4e5222b7 plus one platform-objects / plugin-audit commit; nextUtcCalendarDay (spec) and memory-driver.ts are untouched (file list); D-D1 does keep the conversion operator-blind and put the rewrite on the emitters; the reach grep gives 0 non-test hits against 31 test hits, and service-analytics plugin.ts:245 is a comment; the four sibling-face citations resolve (strategies/native-sql-strategy.ts:1244 widens values[0] then coerces nextDay; strategies/objectql-strategy.ts:548 widens bounds.$lte; preview-evaluator.ts:104 lteBound is type-blind; driver-mongodb mongodb-filter.ts:1165 widens value then store(nextDay)); the two commits behind are 9b384f63ae and 5757463712, neither in driver-memory, spec or core; eslint.config.mjs:327 says the config never enables type-aware linting. TRUE throughout. The test, gate and ablation counts are the dev's own measurements and were not re-run here; the head's check-runs are the verdict on them.

③ Boundary flags

  • H2 ($between refused on this face, no row added) — ANSWERED, accepted: not in MONGO_TO_CUBE_OPERATOR; the INVALID_FILTER / 400 refusal is pinned on both exits. Triage's "one not reachable on this face is reported with the measurement" is satisfied.
  • H3 (dateRange end already widened the raw end; pinned, not changed) — ANSWERED, accepted: ①7.
  • H5 (declared-datetime last supported day broken by the same order, fixed and pinned) — ANSWERED, accepted: ①2; same order defect, inside the card's scope, find()-equal.
  • Reach (no non-test new MemoryAnalyticsService( in the repo) — ANSWERED, verified on the head: 0 non-test, 31 test. p2 stands; no door was found, so no regrade.
  • Other analytics and preview faces read (not run) as not sharing this order — ANSWERED, verified by reading at the cited lines; labelled "not measured at runtime" in the PR body, which is honest; nothing to escalate.
  • Deviation, origin/main not merged forward (2 behind) — accepted: the two commits touch platform-objects translations and service-storage docs only; CI runs the merge ref.
  • Deviation, three gates first exited 3 (prerequisite) and were re-run green after a build — accepted: a prerequisite, not a verdict; the head's check-runs cover the same families.
  • Deviation, commit trailers — accepted: the three commits carry the model-free Claude-Session plus Co-authored-by: Claude pair AGENTS.md prescribes, which outranks the harness reminder; the PR body carries the session-URL footer AGENTS.md assigns to PR bodies.
  • open_questions: empty. out_of_scope_findings: empty. Nothing to escalate.

Check-runs on 8cae2ae3 as read for this record: 31 runs — 16 success (Build Core, Test Core 6/6, Type Check source gates, Type Check debt ledger, Check Changeset, Check PR Size, Governed Surface Queue Guard, Check Documentation Links, Flag docs affected by code changes, Auto Label, filter, Dogfood Verify CLI, and the four card / branch claim checks), 3 skipped (Console Pin Gate, Build Docs, Packed-tarball smoke), 12 in_progress (Test Core 1-5 of 6, Dogfood Regression Gate 1-3 of 3, Temporal Conformance, Type Check consumer gates, Type Check workspace, Lint and Repo Gates), 0 failure; the Vercel commit status is success. Their conclusions are the gate verdicts; this record does not wait on them, and the landing still needs every one green.

Implemented-by: claude/issue-20661-memory-analytics-whole-day-first
Reviewed-by: session_01DEvba2nBuD4tWzfq8r8NFY

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 20:43
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit d3f88fa Sep 29, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20661-memory-analytics-whole-day-first branch September 29, 2026 21:01
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

2 participants