Skip to content

skills: a vetted hook template refuses two records of one person over overlapping time — ruled: no exclusions key, no kernel change (6876330213) #32979

Closed

Description

@os-bob

Filing category: ② a decision only the maintainer can make.

维护者速读

  • 现象:加班申请里,和一张已批准加班完全相同时段的新申请照样能建、能提交;如果再批一次,调休就记了两遍。请假单同理(同一天请两次)。校验规则只能看当前这一条记录,读不到同一个人的其他记录,所以 AI 和人都写不出"时间不能重叠"的规则。
  • 要你裁:平台是否提供"同一人时间段不重叠"的声明式约束。
  • 推荐:A:对象级 exclusion 约束(分组字段 + 起止字段 + 状态过滤),写入时由服务端查询判定,与唯一索引同一层。

One-sentence problem

Every time-booking app (overtime, leave, room booking, shifts) needs "no two of mine overlap", and the platform's rules cannot see the other records.

Evidence (reach)

Hosted tenant (pnpm dev:stack --with-ui, Team plan; cloud ff373e02, framework d7c3d44e), member's MCP OAuth token:

  • el5z_ot_req "11月10日版本上线加班" 18:30–21:30, approved, 3 h credited.
  • create_record el5z_ot_req { start_time: '2026-11-10T18:30:00+08:00', end_time: '2026-11-10T21:30:00+08:00', compensation: 'comp_leave', … } → created (draft), submittable.
  • A validation condition sees record, previous, one-hop references; no query over sibling rows. Unique indexes (indexes[].unique) compare equal values only.

Options

Option What it does What the customer sees Effort
A Object-level declarative constraint, e.g. exclusions: [{ name, groupBy: ['owner_id'], range: ['start_time', 'end_time'], where: { status: { $ne: 'rejected' } }, message }], checked on insert/update by the data layer with a range query "该时段已有加班申请" on save, from AI or form data + spec seats · 2–3 PRs · precedent: unique indexes
B Let validation scripts call a bounded aggregate, e.g. count('el5z_ot_req', { owner_id: record.owner_id, … }) > 0 Same message; authors write the query formula + data seats · 2 PRs · opens queries inside every validation
C Keep as is; approvers catch duplicates Double credit unless a person notices none

Business reading: A covers the common case with one declared line; B is general but every rule becomes a query; C leaves money/time to human attention.

Recommendation

A. It is the pattern behind every booking app, it stays declarative (lintable, explainable to an agent), and it lives where unique indexes already live. Confidence gap: whether where should be a status filter only or a full filter; recommend the same filter shape summaries use.
Self-check: ① A; ② A or B; ③ A (an agent cannot write B wrong in a way that silently passes); ④ A smaller than B.

After the ruling

A: spec key on the object, data-layer check on write with a range query over the group, lint for unknown fields, AI authoring rule; e2e: second overlapping overtime is refused, an adjacent one (21:30–23:00) is accepted.

os-decision-facets
① 项目长远合理性: A 与唯一索引同层、声明式,可被 lint 与 AI 规则覆盖。
② 实际业务拉动: 加班、请假、会议室、排班都需要;本次实测重复加班可提交。
③ 防 AI 犯错: A 由平台判定;B 让 agent 写查询,易写错且难核验。
④ 创业阶段不扩散: A 一个新约束类型;B 给所有校验开查询能力。
Prior rulings read: none found; thread: none

Reader who acts: the data seat and the spec seat, after the ruling.
Dedupe: repo:objectstack-ai/objectstack is:issue overlapping time range validation cross-record → 11 hits, none on this; validation query other records uniqueness overlap → 14 hits (#8413 closed, turso unique) — none on this.

Activity

  1. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Nov 13, 2026
  2. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    Contributor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: audit · needs-user-decision · domain:spec · p2 · 2026-11-13T01:46Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    Contributor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Brief-claimed-by: session_01Q4EjjMnyLqJdzuktEmRPXH · batch #2 · 2026-11-13T04:46Z

    Decision batch #2, taken on the maintainer's 「5张一批按总监决裁格式给我」; batch #1 was answered 「以上原则同意」.

  4. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    Contributor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Ruling recorded · #32979 · A, after 18.4.0 · 2026-11-13T05:20Z

    Maintainer, verbatim (triage#1 session): 「32677 暂停,先由元数据项目自己处理。其他同意」, answering decision batch #2 (proposed 32627 1B·2B·3A / 32749 A / 32979 A / 32677 B / 32688 C). The batch was analysed independently at the fable tier, with prior rulings checked first.

    • A: an object declares an exclusion constraint (a group-by field, start and end fields, and a status filter in the existing conditional-index form). The data layer enforces it at write time, atomically, at the same layer as the unique index. It is not a validation rule: validation.zod.ts says a SELECT-then-INSERT rule is inherently racy and validation does no I/O.
    • Attached conditions:
      • A driver that cannot make it atomic refuses the declaration loudly.
      • Intervals are half-open (21:30 end and 21:30 start do not overlap).
      • The design states what happens to existing overlapping rows.
    • Execution: domain:spec writes a one-page design (key shape, interval semantics, existing data, per-driver atomicity) through contract review. Then spec lands the key and authoring checks, and data lands enforcement per driver. Acceptance: a second overlapping record is refused, an adjacent one is accepted, and of two concurrent requests only one succeeds. Scheduled after the 18.4.0 cut: needs-user-decision → pm:on-hold.

    Restart-when: 18.4.0 is cut (the maintainer's ruling A here, scheduled after the release).


    Generated by Claude Code

  5. added
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    and removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Nov 13, 2026
  6. changed the title [-][Decision] How should an app stop the same person filing two records for overlapping time (overtime twice for one evening, leave over leave)?[/-] [+]data layer: a declarative exclusion constraint refuses two records of one person over overlapping time (e.g. overtime twice) — ruled A, after 18.4.0[/+] on Nov 13, 2026
  7. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    Contributor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: restart → pm:queue · 2026-11-13T23:07Z

    • Ruling A, after 18.4.0 (5:20Z record). 18.4.0 was published on 2026-11-13 (run 43415674353, npm and GitHub Releases), so the condition is met. Grade unchanged (p2). The card is retitled to the ruled work.
  8. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    on Nov 13, 2026
  9. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    Contributor

    domain:spec#2 · os-tesla · session_01L9qibvfX5bawZKtfa6u7j9

    Claim: PM loop round 3 · 2026-11-14T00:26Z
    Session: session_01L9qibvfX5bawZKtfa6u7j9 (seat); card thread session_01L9qibwo1JzKTz7boR9JXuG is the dev
    Account: os-tesla
    Branch: claude/project-thread-9apm8i (the card thread's designated branch; the platform lets a thread push only that branch, so not claude/issue-32979-*)
    Worktree: objectstack-issue-32979
    Domain: domain:spec
    Seat: domain:spec#2
    File surface: step 1 only: docs/design/exclusion-constraints.md (new) and a docs/design/ index row if one exists. ⛔ No schema, lint or driver code; docs/adr/** untouched (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default tier, effort: high (fleet token rule 2, maintainer 2026-11-12); the design goes through contract review at tier
    Clause-②: no
    Metadata-protocol: none
    Responsibility: no declarative way to refuse two records of one person over overlapping time; validation sees one record and unique indexes compare equal values only (packages/spec, data drivers) (objectstack) | none: ruling A (05:20Z record) schedules a design first | overtime, leave, booking and shift app authors, and the agents that author them
    Thread-read: 6867197231
    Premise: holds at origin/main 68972cd2: ruling A's condition "after 18.4.0" is met (triage restart 23:07Z); the ruling's Execution line puts a one-page design through contract review before any key or enforcement lands. Steps 2 (spec key + authoring checks) and 3 (data enforcement per driver) get their own orders from the accepted design.
    Serial constraints cleared: no overlap with this seat's in-flight #33072 (PR #33119; lint and metadata-protocol).

  10. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 14, 2026
  11. 10 remaining items

  12. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    on Nov 14, 2026
  13. os-tesla commented on Nov 14, 2026

    @os-tesla
    Collaborator

    Correction to the comment above: this card goes to pm:queue, not pm:on-hold. The maintainer clarified that the current pause stops dispatch from the queue; ruled cards still enter it. Item 1 (the revert) is #33239; item 2 (the hook template) waits for dispatch.


    Generated by Claude Code

  14. os-tesla commented on Nov 14, 2026

    @os-tesla
    Collaborator

    Landed: PR #33239 merged at 295ce12, removing docs/design/exclusion-constraints.md (the design for the exclusions key that the ruling above did not adopt). git ls-tree origin/main docs/design/exclusion-constraints.md now returns nothing.

    What remains on this card: the vetted overlap hook template in docs/skills, per the ruling. The card stays open in pm:queue for that; skills/** is a Tier H surface, so that PR needs a maintainer approval when it lands.


    Generated by Claude Code


    Generated by Claude Code

  15. changed the title [-]data layer: a declarative exclusion constraint refuses two records of one person over overlapping time (e.g. overtime twice) — ruled A, after 18.4.0[/-] [+]skills: a vetted hook template refuses two records of one person over overlapping time — ruled: no exclusions key, no kernel change (6876330213)[/+] on Nov 14, 2026
  16. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    Contributor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: retitled and re-laned to the new ruling · domain:spec → domain:skills · 2026-11-14T13:46Z

    ⛔ Not a claim; the grade is unchanged (p2).

    • The maintainer ruled on 6876330213: overlapping-booking rules stay in hook scripts for now, with no exclusions key and no kernel change, and a vetted hook template to follow in a docs change. The step-1 design doc was reverted (PR docs(design): revert the exclusion-constraints design doc #33239). My earlier title described the option that was not adopted, so it is replaced.
    • What remains is a skills or docs hook template, so the card moves to domain:skills.
  17. added
    domain:skillsDomain lane — seat card indexed by label:pm:seat
    and removed
    domain:specDomain lane — seat card indexed by label:pm:seat
    on Nov 14, 2026
  18. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    Contributor

    domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

    Claim: PM loop round 1
    Session: session_01FEkQ5pjSPT7ui2EUcunaVb
    Account: os-project-manager
    Branch: claude/project-thread-0sjbar
    Worktree: objectstack-issue-32979
    Domain: domain:skills
    Seat: domain:skills#1
    File surface: one skills/objectstack-* rules/references file that already teaches hooks (+ its eval if expected behaviour moves), one content/docs/** hooks page, the proving test, .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: CONTRACT_REVIEW_TIER (MANDATORY: skills/** hit in dispatch-gates --tier), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the platform has no cross-record overlap check (ruled: stays in hook scripts, 6876330213) | none — no vetted template exists | every time-booking app an AI author writes
    Thread-read: 6878198537
    Premise: holds
    Serial constraints cleared: #33235 (also skills/**, objectstack-ui files) — disjoint; PR #33239 (item 1) merged

    Order: /mnt/project-files/pm-orders/skills/order-32979.md (project file). Ruling-ref: the maintainer's summons 「开一个 skills 车道,派发 p1 p2」 (2026-11-14T15:13Z, this account's project).

  19. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 14, 2026
  20. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    Contributor

    domain:skills#1 · os-project-manager · session_01FEkQ5kKeYvkiYDEFCNhNhU
    os-dev-report
    {
    "issue": 32979,
    "status": "done",
    "branch": "claude/project-thread-0sjbar",
    "pr": "#33292",
    "session": "session_01FEkQ5kKeYvkiYDEFCNhNhU",
    "premise_still_valid": true,
    "summary": "Ruling 6876330213 item 2: skills/objectstack-data/references/data-hooks.md (the file that already teaches hooks) gains pattern 5, a beforeInsert + beforeUpdate overlap-check hook with a sandboxed body: stored row merged under the patch, the record itself excluded with $ne, ignored statuses named with $nin (fail closed), half-open ranges, a declared refusals entry OVERLAP 409. Its limits sit beside it: the ruling's three plus the read-then-write race. content/docs/automation/hooks.mdx gains the same template with a reason per rule (+62 lines). The skills file pays for itself: five Key Properties subsections that restated the defineHook example comments, the Before vs After table and Best Practices are deleted, file 943 to 907 lines, 10007 to 10005 tokens, no ceiling raise. No spec, engine, data-layer or runtime change. Head 500642a.",
    "tests": "packages/runtime/src/sandbox/overlap-hook-template.integration.test.ts runs the template on a real ObjectQL + SQLite driver + QuickJSScriptRunner + bindHooksToEngine, read off mapDataError (409, declaredCode OVERLAP): overlapping insert refused; null or unlisted status sibling still counts; adjacent, other owner and rejected rows accepted; update moving into overlap refused (stored row merged) and reviving a rejected row refused; self update not refused. Head 500642a under os-verify-lock: Test Files 1 passed, Tests 5 passed, VERDICT command-exit 0. Mutation proof via ablation-replace.mjs on the test copy, restored to HEAD blob 90ec0c34 each time: drop the $ne self-exclusion 2 failed; $lt to $lte 1 failed; $nin to $in approved 1 failed; drop ctx.previous from the merge 1 failed. pnpm --filter @objectstack/runtime typecheck (tsc + check:test-typecheck) exit 0.",
    "gates": "dispatch-gates --commands at 500642a derived 97 commands, exit codes captured before any pipe. 94 exit 0 first pass. check-plugin-teardown-shape --self-test exit 3 (shallow clone) then exit 0 after git fetch --depth=1 of the pinned commit. spec check:skill-examples exit 3 (client-react unbuilt) then exit 0 after building client-react and its closure under the lock: 345 prose examples type-check, new os:check block included. check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (41 packages without dist), NOT MEASURED; no package source touched, CI runs it. --ran reconciliation: 97 derived, 96 run, 1 NOT-MEASURED, 0 UNRUN, exit 0 (record: project file pm-orders/skills/32979-gates-ran.txt). check-skills-token-ratchet exit 0 (data-hooks.md 10005 of 10011). dispatch-gates --tier: MANDATORY CONTRACT_REVIEW_TIER (skills/). check-governed-merges --branch: exit 3 GOVERNED, Tier H, skills/ x1, 348 changed lines. CI check-runs on PR 33292: in_progress at report time.",
    "line_budget": "skills/** net -36 lines (budget +45); docs page +62 lines (budget +80)",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "6 — mcp__github__issue_read (get x1, get_comments x2), mcp__claude-code-remote__get_session x1, plus 2 hearthbot thread messages; 0 GitHub write tools",
    "api_writes": "2 — fleet-write relay pr_create (run 43612654278, PR 33292, body read back identical); this os-dev-report comment via post-stamped",
    "deviations": [
    "Standing lines ask for a trailing signed HTML comment in the PR body; os-dev.md forbids dev-written HTML comments, and the standing lines lose to it where they conflict, so the body ends with a plain Signed: line instead.",
    "pr_create sent no labels key: the order names no label and the relay refuses an empty labels array.",
    "The four mutation runs and one probe run went outside os-verify-lock (single-file vitest, about 10 s each)."
    ],
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": [
    "carrier: none · the proving test holds a byte-identical copy of the template rather than reading skills/, because a read needs scripts/cross-package-test-inputs.mjs and packages/runtime/vitest.repo-tests.json, outside the claim's file surface; a seat may widen it to make drift impossible",
    "carrier: none · ctx.api reads as the saver (runAs inherit), so with a group key whose rows the saver cannot read (a room under private sharing) the count can miss them; runAs system is the documented remedy; not measured, not in the template, noted in the PR acceptance notes"
    ]
    }

  21. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    Contributor

    domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

    ACCEPT → PR #33292 comment 6881847814 · contract review PASS 6881830735 · held for the maintainer (Tier H; reviewers requested) · 2026-11-14T18:00Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

domain:skillsDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions