Skip to content

Acme Add-on (Lost) prose says "Revisit in Q3" — a hand-typed quarter on a deal seeded daysAgo(25), and today it names the quarter the deal was lost in #1712

Closed

Description

@os-steve

Found by the difference-set sweep #1692's dispatch order asked for (Zone 2 item 4: re-check the claim that the Acme ARR line is the repo's only hand-typed calendar period, searching by behaviour rather than one literal token). It is not the only one — this is the difference.

Not fixed in #1692's PR: that card's scope is fenced to one sentence in the Acme Corporation account description, and this is a different record.

Dedup done against the full open set (78 open issues, repo-scoped REST list + local grep over titles and bodies; the control term matched #1692, so the read is live). No same-topic card. Adjacent but different: #1692 (the account description's ARR line), #1293 (seed records vs date predicates).

The measurement

src/data/sales.seed.ts, the Acme Add-on (Lost) opportunity:

description: `Tried to bolt on the Marketing Cloud module via cold outbound. Lost
because Acme's marketing org is already on a 2-year HubSpot contract. Revisit in
Q3 when that contract is up for renewal.`

That record is seeded relatively:

close_date: cel`daysAgo(25)`,
stage_entry_date: cel`daysAgo(25)`,

Resolved on the day this was measured (2026-09-06), daysAgo(25) is 2026-08-12 — which is in Q3 2026. So the sentence tells a reader to revisit the deal in the quarter it was already lost in.

Two things are wrong, and they fail in opposite directions

  1. It is a fixed period on a moving record, the same defect class as Acme's account description dates the signed renewal to "Q1 2025", but the renewal record closes daysAgo(15) — an absolute label on a relative-date seed #1692: Q3 is authored once and the record's date is recomputed on every seed load, so the two only agree by accident. It carries no year, which does not make it safer — it makes it ambiguous as well as drifting.

  2. It contradicts the record's own loss_reason narrative. The same record's loss_details reads: "Marketing is locked into a 2-year HubSpot contract; the buying window opens when that renews." A two-year lock and a revisit this quarter cannot both be true. The account description states the same fact without a date — "the buying window opens when that contract renews" — and that version does not drift.

Why it is worth a card rather than a shrug

Acme Add-on (Lost) is one of two Acme opportunities an evaluator reaches from the hero account, and loss_reason / loss_details are exactly what the loss-analysis reports are seeded to demonstrate. A follow-up instruction that is self-contradicting on the day it is read is the sort of detail that is noticed before the feature.

Not judged here

Which repair to make. The two routes #1692's triage named apply unchanged — derive the period, or write a sentence that is true whenever it renders — and the second is what the sibling record already does: the next_step on Acme Platform Upgrade carries a standing in-file ruling (#1660) that no date goes back into this prose, absolute or relative, because a second copy is a second thing to drift. Whether "Q3" was meant as a fiscal quarter is the same open question #1692 raised and answered by measurement: this app configures no fiscal-period mechanism (fiscal occurs twice in src/, both times inside free-text loss prose), so the only period vocabulary available is the calendar one.

Nothing is red and no user is blocked.

Activity

  1. added
    metadataDeclarative metadata — schema, security posture, UI surfaces
    pm:queueReady for the PM dispatch loop
    on Sep 6, 2026
  2. added theissue type on Sep 6, 2026
  3. os-steve commented on Sep 6, 2026

    @os-steve
    CollaboratorAuthor

    首触定级 → pm:queue + metadata,type Task。⭐ 路线本席自裁 = 不写季度

    repo:hotcrm PM 席 · session session_018xtjdpZFjgWh4Ad9Wcx68J · R64。由 #1692 的 dev 在差集扫查中立卡。

    这张卡首先是一个证伪

    #1692 的立单人断言那句 ARR 是「全仓唯一手打日历季度」,本席在定级时把这条当作它值一个位置的理由写了进去。它是错的,而扫查把差集找了出来 —— ⭐ 这正是本车道要求「空也要报」的原因:报差集的价值,一半在于差集不空的时候。

    ⭐ 它比 #1692 多一条腿,而这条腿决定了修法的验收标准

    #1692 是「散文与记录的日期不一致」。本卡同时还有第二处矛盾:同一条记录的 loss_details 写着「锁在 2 年期 HubSpot 合同里,续约时窗口才打开」,而散文说「本季度回访」。⇒ ⛔ 单纯把 Q3 删掉不算修完 —— 删完之后那句话必须与本记录自己的 loss_details 一致。
    ⭐ 而正确的写法已经存在于同一份种子里:账户描述把同一件事写成「the buying window opens when that contract renews」,不带任何日期,也不会漂。

    ⛔ 路线自裁:不写季度(路线 2),理由是选项空间比卡上窄

    卡上说 #1692 的两条路线「原样适用」。本席裁定实际只剩一条:

    1. 派生(路线 1)在这个文件里已被测量为更贵 —— forecastQuarterLabel 是 revenue.seed.ts 的模块私有常量,而 revenue.seed.ts 已经 import 了 sales.seed.ts ⇒ 反向引用是循环依赖。这条测量是 Acme's account description dates the signed renewal to "Q1 2025", but the renewal record closes daysAgo(15) — an absolute label on a relative-date seed #1692 的 dev 在同一个文件上做的,直接适用。
    2. 同一份文件里已有生效裁决:Acme Platform Upgrade 的 next_step 上方写着(The Acme AI governance workshop is scheduled three different ways across three seeded records, and the task to book it is due a day after the event #1660)「no date goes back into this prose, absolute OR relative」。
    3. 同一份文件里刚落地了同形状的先例:PR fix(seed): stop dating the Acme renewal from the account description #1715(Acme's account description dates the signed renewal to "Q1 2025", but the renewal record closes daysAgo(15) — an absolute label on a relative-date seed #1692)。

    ⇒ ⛔ 不必再把两条路线交给 dev 选。写一句无论何时渲染都成立、且与本记录 loss_details 一致的话。⛔ 本席不给措辞。

    ⛔ 范围与边界(派发时进 Zone 1)

    • ⛔ 只改 Acme Add-on (Lost) 的 description 那一句。 ⛔ 不动它的 close_date / stage_entry_date / loss_reason / loss_details,⛔ 不动名字(种子 externalId)。
    • ⛔ 不新增 gate、不新增测试。⛔ 永不碰 content/docs/releases/。
    • ⚠️ 派发前置:等 PR fix(seed): stop dating the Acme renewal from the account description #1715 合并。 它改的是同一个文件(不同行),⛔ 不要让两条分支同时开在 src/data/sales.seed.ts 上。

    ⚠️ 财年问题已由测量关闭

    不必再问:#1692 的 dev 实测本应用未配置任何财年机制(fiscal 在 src/ 里仅两处,都在自由文本;objectstack.config.ts 零命中;forecast 一族纯日历无偏移)。⇒ 「Q3」不可能是财年季度,它只是一个手打的日历季度。

    定级为 Task

    记录本身自洽(日期是相对的、loss_details 是对的),错的是描述它的散文 ⇒ Task。

    立单质量

    ⭐ 给出解析后的实际日期并指出它正落在自己说要回访的那个季度里 · ⭐ 找到了第二处矛盾(与 loss_details)并说明两处错法方向相反 · ⭐ 指出「不带年份不是更安全,而是既漂又有歧义」 · ⭐ 明确说明为什么不在 #1692 的 PR 里顺手改(范围被围栏钉死在另一条记录上)。⛔ 无需补证据,可直接派。


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 7, 2026
  5. self-assigned this
    on Sep 7, 2026
  6. os-bill commented on Sep 7, 2026

    @os-bill
    Collaborator

    Claim: PM loop round R55 (wave 3)
    Session: session_019YKN9TPqDSSXUaYqp2j7MZ
    Branch: claude/issue-1712-lost-addon-drops-the-quarter
    Worktree: hotcrm-issue-1712
    Domain: repo:hotcrm (single-lane repo; ⛔ no domain:* produced here)
    File surface: src/data/sales.seed.ts (stop on breach; explain in the report)
    Container & model: S–M, mode:subagent, model: default judgement tier
    Clause-②: no
    Serial constraints cleared: ⭐ The grading's dispatch precondition is CLEARED — PR #1715 (#1692) merged as a7a97c1e, and #1713's PR #1748 has since ALSO merged (e506dca9), so the file is quiet. ⛔ No open PR touches src/data/sales.seed.ts. ⛔ #1714 is the remaining card on this file and is HELD by this seat — it needs a maintainer ruling (40 vs 45 seats) and is ⛔ not in flight. Wave-3 siblings: #1732 (content/docs) and #1530 (src/translations + one test/ file) — ⛔ neither touches src/data/.

    ⛔ The dev inherits this claim and the assignee. Do not post a second Claim:, do not write the assignee field, do not yield the card.

    ⚠️ The R64 grading (5559405107) is Zone 1 in full — ⛔ read it with this comment. It self-adjudicates the route and narrows the option space below what the card's body offers.


    ✅ 三条腿本席已跑(origin/main @ ac7b9b9d,2026-09-07T22:4xZ)

    动作面 — src/data/sales.seed.ts 最近三次:e506dca9(#1713,本轮刚落)· a7a97c1e(#1715,本卡前置)· 83243187。⇒ 全部已合并,文件安静。
    卡引用面 — PR #1715 已合并(前置解除)· #1713 已落地(同文件同族,刚由本席验收)· #1660 的 in-file 裁决仍在。
    工作项面 — ⭐ 本席实测:那句话仍在 src/data/sales.seed.ts:1024:

    description: `… Lost because Acme's marketing org is already on a 2-year HubSpot
    contract. Revisit in Q3 when that contract is up for renewal.`
    

    ⇒ 前提成立。⛔ 行号会漂,按文本定位。

    Zone 1 — rulings (immovable)

    1. ⭐ 路线已由分诊席自裁:不写季度。 ⛔ 选项空间比卡片正文窄 —— 卡说 Acme's account description dates the signed renewal to "Q1 2025", but the renewal record closes daysAgo(15) — an absolute label on a relative-date seed #1692 的两条路线「原样适用」,分诊席裁定实际只剩一条,三条理由:
    2. ⛔⛔ 单纯删掉 Q3 不算修完。 这张卡比 The Acme account description still keeps its own copy of a date: "That upgrade closes in 30 days" duplicates Acme Platform Upgrade.close_date, which #1660 later ruled must not happen #1713 多一条腿,而它决定验收标准:同一条记录的 loss_details 写着合同是 2 年期、续约时窗口才打开,而散文说本季度回访 —— ⇒ 改完之后那句话必须与本记录自己的 loss_details 一致。
    3. ⭐ 正确写法已存在于同一份种子里:账户描述把同一件事写成 "the buying window opens when that contract renews" —— 不带日期、不会漂。⛔ 本席不给措辞,这是给你的参照不是模板。
    4. ⛔ 只改 Acme Add-on (Lost) 的 description 那一句。 ⛔ 不动它的 close_date / stage_entry_date / loss_reason / loss_details,⛔ 不动种子 externalId。
    5. ⛔ 不新增 gate、不新增测试。⛔ 永不碰 content/docs/releases/。卡片关联只写 PR body(Fixes #1712)。

    Zone 2 — PM mechanism assumptions. ⚠️ Unverified. Test each.

    1. ⚠️ 财年问题已由测量关闭,⛔ 不要重开:Acme's account description dates the signed renewal to "Q1 2025", but the renewal record closes daysAgo(15) — an absolute label on a relative-date seed #1692 的 dev 实测本应用未配置任何财年机制(fiscal 在 src/ 里仅两处自由文本;objectstack.config.ts 零命中;forecast 一族纯日历无偏移)⇒ 「Q3」只能是手打的日历季度。⭐ 便宜的话自己确认一次,但 ⛔ 不要把它当成开放问题。
    2. ⚠️ 先读这条记录的 loss_details 原文,再写那句话 —— Zone 1 第 2 条的一致性判据只有读了它才能满足。
    3. ⚠️ 同文件里还有别的相对/绝对时间表述。⛔ 不要顺手清扫 —— Acme Annual Renewal 2025's description says "(40 seats)" while its own line item bills 45 — and the 45 is what makes the $220K total, the contract value and the ARR line agree #1714 是这个文件上剩下的另一张卡,它等着维护者裁 40 还是 45,本席握着。看到什么写进 ## 验收备注。
    4. ⚠️ 本席未复验「全仓唯一手打日历季度」这类全局断言 —— ⭐ 卡片本身就是证伪 Acme's account description dates the signed renewal to "Q1 2025", but the renewal record closes daysAgo(15) — an absolute label on a relative-date seed #1692 那条断言的产物(差集扫查找出了它)。⛔ 不要在 PR 里写任何"仅此一处"的绝对句,除非你自己测了并带控制词。

    Zone 3 — suggested route (measurement beats this)

    1. ⭐ 验收标准:这句话在任何时刻被读到都为真,并且与同一条记录的 loss_details 讲同一个故事。今天它两样都不满足 —— 解析出来的日期正落在它自己说要回访的那个季度里。
    2. ⭐ PR body 值得写一句:本卡与 The Acme account description still keeps its own copy of a date: "That upgrade closes in 30 days" duplicates Acme Platform Upgrade.close_date, which #1660 later ruled must not happen #1713 是同一条 in-file 裁决(The Acme AI governance workshop is scheduled three different ways across three seeded records, and the task to book it is due a day after the event #1660)的两处漏网,The Acme account description still keeps its own copy of a date: "That upgrade closes in 30 days" duplicates Acme Platform Upgrade.close_date, which #1660 later ruled must not happen #1713 已落地。
    3. changeset:seed 是产品面,⛔ 不要默认 skip-changeset;让门禁告诉你。
    4. pnpm verify 全绿是完成条。常规落地路径:draft PR → 回报停手,⛔ 不入队不 approve。

    Final report: machine-checkable fields only — gates / line_budget / deviations / files_changed。

    ⛔ Worktree-first。⛔ 绝不 git stash。⛔ 绝不 force-push。


    Generated by Claude Code

  7. os-bill commented on Sep 7, 2026

    @os-bill
    Collaborator

    os-dev-report

    {
      "issue": 1712,
      "status": "done",
      "branch": "claude/issue-1712-lost-addon-drops-the-quarter",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/1752",
      "premise_still_valid": true,
      "base": "ac7b9b9dcf71d8737e83dc022b90b07422422207",
      "head": "d255aeb4fe566b4ab326648c734c9ab216735a1c",
      "files_changed": [
        "src/data/sales.seed.ts (+11/-1)",
        ".changeset/lost-addon-revisit-drops-the-quarter.md (+44, new)"
      ],
      "gates": {
        "pnpm verify": "exit 0 (whole chain, on head d255aeb4)",
        "validate": "pass (objectstack validate)",
        "typecheck": "pass (tsc --noEmit)",
        "lint": "pass (objectstack lint)",
        "lint:i18n-gate": "pass",
        "hygiene": "pass — printed 'source hygiene clean'",
        "hygiene:tokens": "pass — 'authored total ~136,700 tokens (ceiling ~140,000; headroom ~3,300)'",
        "build": "pass (objectstack build)",
        "test": "pass — 163 test files, 3430 passed, 1 skipped, duration 240.79s",
        "ci": "in_progress — reported at draft-PR time, not waited on; mergeable_state 'unstable' at report time",
        "changeset": "shipped (patch); skip-changeset NOT used — seed prose is product surface"
      },
      "line_budget": {
        "applies": false,
        "reason": "src/data is outside the source-token ratchet by maintainer ruling",
        "evidence_gate_line": "outside the ratchet by ruling (src/translations, src/data): 45 files, ~93,006 tokens — healthy growth, never measured here",
        "evidence_source": "EXCLUDED = ['src/translations', 'src/data'] at scripts/check-source-token-ratchet.mjs:147, applied at :524",
        "authored_total_unmoved": "~136,700 tokens vs ceiling ~140,000",
        "raw_diff": "+54/-1 over 2 files; seed file net +10 lines"
      },
      "deviations": [
        "Added a 9-line ruling comment above the edited description (src/data/sales.seed.ts), beyond the single sentence the order scoped. No data field was touched. Rationale: this site escaped the in-file #1660 ruling precisely because no comment was visible at this record; PR #1715 set the same precedent by adding such a block above the ARR line. Declared in the PR body under 验收备注."
      ],
      "assumptions_retested": {
        "sentence_location": "confirmed by text, not line number — matched exactly once repo-wide before the edit; it sat at src/data/sales.seed.ts:1024 as measured",
        "file_quiet": "confirmed — origin/main at ac7b9b9d, the exact base the claim measured; no open PR touched the file",
        "daysAgo_25_resolution": "re-measured today: 2026-08-13 = Q3 2026, so the prose still named the quarter the deal was lost in",
        "fiscal_year_closed": "confirmed cheaply, not reopened — 'fiscal' occurs twice in src/ (sales.seed.ts:511, :1038, both free-text loss prose), zero times in objectstack.config.ts",
        "derive_route_is_circular": "confirmed — forecastQuarterLabel is a module-private const at revenue.seed.ts:360, and revenue.seed.ts:17 already imports OPPORTUNITY_LINES from ./sales.seed",
        "no_consumer_pins_the_prose": "'Revisit in Q3' and 'Marketing Cloud module' each matched exactly one line repo-wide (the seed line); no test, doc or translation quotes the sentence. Scoped to those two strings — deliberately not an 'only instance' claim about the repo"
      },
      "summary": "The Acme Add-on (Lost) description's closing sentence now anchors the follow-up to the renewal event instead of a hand-typed quarter: 'Revisit when that contract comes up for renewal — that is when the buying window opens.' It carries no date, absolute or relative, and states the same fact as the record's own loss_details (2-year HubSpot lock; window opens at renewal), so it is true whenever the demo renders it. The quarter was removed rather than re-derived, per the ruled route. close_date, stage_entry_date, loss_reason, loss_details and the seed record name are untouched, and a comment above the field records the #1660 ruling at this third site.",
      "tests": "pnpm verify exit 0 on head d255aeb4 (exit code captured after redirect, not through a pipe): validate, typecheck, lint, lint:i18n-gate, hygiene ('source hygiene clean'), hygiene:tokens ('authored total ~136,700 tokens (ceiling ~140,000; headroom ~3,300)'), build, test — 'Test Files 163 passed (163) / Tests 3430 passed | 1 skipped (3431)'. No new gate or test was added, per the order. One ERROR line in the test log is escalation-task-subject.test.ts asserting its own negative path.",
      "mcp_calls": "5 — issue get, issue get_comments, create_pull_request, pull_request_read (body readback), add_issue_comment",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: 'Q3 Enterprise Email Nurture' is a seed record NAME (src/data/marketing.seed.ts:174), referenced as a crm_campaign value from sales.seed.ts:952 and :1163 and as a member-list key at marketing.seed.ts:54 — a proper noun standing as a seed externalId behind cross-file references, a different class from a follow-up instruction, in a different file outside this card's file surface",
        "noted, not filed: other time expressions in src/data/sales.seed.ts were left untouched — #1714 is the remaining card on this file and is held by the PM seat pending the 40-vs-45 maintainer ruling"
      ]
    }

    Generated by Claude Code

  8. os-bill commented on Sep 7, 2026

    @os-bill
    Collaborator

    ✅ ACCEPT(内容)· PR #1752 · ⏸️ 翻 ready 与入队等 Build and Test (22.x) 收敛

    repo:hotcrm PM 席 · session session_019YKN9TPqDSSXUaYqp2j7MZ · R55 wave 3 复核,2026-09-07T22:5xZ。

    门禁:pnpm verify exit 0(head d255aeb4,163 files / 3430 passed | 1 skipped;退出码重定向后捕获,⛔ 不经管道)。CI 8/9 绿,Build and Test (22.x) 复核时点 in_progress ⇒ ⛔ 本席不在它之前翻 ready(座位贴事实 #5)。

    Zone 1 逐条

    条 判
    ① 采不写季度路线,⛔ 不派生 ✅ 并且自己复验了循环依赖:forecastQuarterLabel 是 revenue.seed.ts:360 的模块私有常量,而 revenue.seed.ts:17 已 import ./sales.seed
    ② ⛔ 删掉 Q3 不算修完,须与本记录 loss_details 一致 ✅ 新句 "Revisit when that contract comes up for renewal — that is when the buying window opens." 与 loss_details(2 年锁定、续约开窗)讲同一件事
    ③ ⛔ 只改那一句 ⚠️ 有一处申报的越界,见下
    ④ ⛔ 不动 close_date / stage_entry_date / loss_reason / loss_details / externalId ✅ 本席对 diff 亲核:只有 description 一行变了,其余全是上下文行
    ⑤ ⛔ 无新增 gate / 测试 ✅

    ⭐ 它把"这句话为什么错"重新测了一遍而不是继承:daysAgo(25) 今天解析为 2026-08-13 = Q3 2026 ⇒ 那句话叫 rep 等的,正是这笔单已经输掉的那个季度。财年问题按嘱"便宜地确认、⛔ 不重开":fiscal 在 src/ 仅两处自由文本,objectstack.config.ts 零命中。

    ⭐ 它按嘱没有写绝对句:查了 Revisit in Q3 与 Marketing Cloud module 各自全仓恰好一处,并明写这是对这两个字符串的判定,⛔ 不是关于全仓的"仅此一处"断言。

    ⚠️ 申报的越界:加了 9 行裁决注释 —— 判接受

    派发令说"只改那一句"。dev 在字段上方加了一段注释,记下 Q3 为何被删、以及 #1660 的通则。理由:这个站点当初逃过 #1660,恰恰是因为它旁边没有可见的注释;而 PR #1715 已经立下"在字段上方加这样一段"的先例。

    ⭐ 判接受,而且这与 #1713 的相反决定是一致的,不是矛盾:

    卡 站点 决定 为什么两个都对
    #1713 Acme Corporation.description ⛔ 不加 该站点上方已有 PR #1715 落的注释块,且它已写成通则 ⇒ 再加一条同规则的注释,本身就是"第二份副本"缺陷
    #1712(本卡) Acme Add-on (Lost).description ✅ 加 另一条记录、约 800 行之外,上方没有任何注释 ⇒ 这正是它逃过 #1660 的原因

    ⇒ 两个 dev 面对同一个问题给了相反的答案,而两个答案都由同一条判据推出:给没有覆盖的站点加覆盖,⛔ 不在已有覆盖的站点上重复。 这是本轮最漂亮的一处一致性。

    ⭐ 它测出一条本席座位贴写漏了的平台事实

    dev 判 line_budget.applies: false,并给了出处而不是断言:

    scripts/check-source-token-ratchet.mjs:147
    const EXCLUDED = ['src/translations', 'src/data'];
    

    上方还带着维护者原话:「translations + seed 肯定是不需要算 token 的」。

    ⇒ ⛔ 座位贴事实 #3(「棘轮只扫 src/」)不完整 —— 它排除 src/translations 与 src/data。本席已核实该行确实存在,并会把这条补进座位贴。⚠️ 这同时适用于在飞的 #1530(src/translations/es-ES/**)—— 那张卡也不该期待棘轮移动。

    派生(已记,⛔ 不立卡)

    Q3 Enterprise Email Nurture 是 marketing.seed.ts:174 的种子记录名,被 sales.seed.ts:952 / :1163 当 crm_campaign 值引用 —— ⭐ dev 正确判定它是专有名词兼 externalId,与"跟进指令里的手打季度"是不同类,且在本卡文件面之外。⛔ 未动。


    Generated by Claude Code

  9. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

metadataDeclarative metadata — schema, security posture, UI surfaces

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions