Skip to content

docs(requirements): repoint REQ-0002's three paths — stale the day they were written (#1933) - #1942

Merged
os-elon-musk merged 1 commit into
mainfrom
claude/issue-1933-req-0002-dead-paths
Sep 15, 2026
Merged

os-elon-musk merged 1 commit into
mainfrom
claude/issue-1933-req-0002-dead-paths

Conversation

@os-elon-musk

Copy link
Copy Markdown
Collaborator

Part of #1933 — the implementation half only, per the PM's 2026-09-15 comment on that card.

⚠️ This body carries no issue-closing keyword, deliberately. #1933 is a decision+fix card
and its policy half stays with the maintainer; the merge queue would otherwise take the card
out of the open-issue inbox that half is waiting in. What is still open on #1933 after this lands:
the ruling on what a requirements record frozen at triage owes when it is also an agent's
authoring input — and, standing on that ruling, docs/architecture/module-split-plan.md,
module-split-inventory.json, and the half of the TREE_DOCS roster that #1923 left hand-written
for exactly this reason.

What this corrects

#1924 / PR #1932 judged 246 dead-path sites one at a time and kept these three, on the ground
that a requirements record is frozen at triage. PM review reversed that single site — the only one
of the 246 where PM and the implementing seat disagreed. The reversal rests on a measurement:
freezing protects a statement that was true when written, and these three never were.

Ancestry reading

The checkout is shallow, so a negative ancestry answer would need a control leg. The reading
below needs none: both legs are positive (exit 0 self-proves in any checkout), and the
load-bearing one is a tree lookup at a single commit, which traverses no ancestry at all.

$ git rev-parse --is-shallow-repository
true

Leg A — the move is an ancestor of the commit that authored REQ-0002
$ git merge-base --is-ancestor 36b27dd 9f13c77 ; echo $?
0
$ git merge-base --is-ancestor 9f13c77 HEAD    ; echo $?      # control leg, known-true
0

Leg B — the three cited paths in the record's OWN tree (shallow-immune)
$ git ls-tree --name-only 9f13c77 src/
src/docs   src/marketing   src/revenue   src/sales   src/service

$ git cat-file -e 9f13c77:src/objects                             ; echo $?   -> 128  absent
$ git cat-file -e 9f13c77:src/flows/opportunity-approval.flow.ts  ; echo $?   -> 128  absent
$ git cat-file -e 9f13c77:src/flows/billing-handoff.flow.ts       ; echo $?   -> 128  absent

positive control — same commit, same command, the paths this PR writes instead
$ git cat-file -e 9f13c77:src/sales/objects                                        -> 0
$ git cat-file -e 9f13c77:src/sales/flows/opportunity-approval.flow.ts             -> 0
$ git cat-file -e 9f13c77:src/sales/flows/billing-handoff-closed-won.flow.ts       -> 0
$ git cat-file -e 9f13c77:src/revenue/flows/billing-handoff-contract-activated.flow.ts -> 0

⇒ 29 minutes after the layout move, the record was written citing three paths the move had
already removed, while all four correct paths were already sitting in the same tree. Stale the day
they were written — a typo, not a record.

REQ-0001 stays frozen, and this measurement is why. Two positive legs, no negative needed:
git merge-base --is-ancestor 3b72afa 36b27dd answers 0 (the shallow graft root precedes the
move) and git cat-file -e 3b72afa:docs/requirements/0001-agency-tier-lead-tagging.md answers 0
(REQ-0001 was already there). Its src/flows/* at line 20 was true when written and is untouched
here. ⚠️ Note for anyone re-running this: git log --diff-filter=A names 3b72afa as REQ-0001's
adding commit, which is a shallow-boundary artifact, not a fact — the real add is beyond the
graft. The two legs above avoid depending on it.

The three repoints

Cited in REQ-0002 present at 9f13c77? now reads present?
src/objects/ no src/sales/objects/ yes
src/flows/opportunity-approval.flow.ts no src/sales/flows/opportunity-approval.flow.ts yes
src/flows/billing-handoff.flow.ts no both successors, below yes

The prefix is per-file, not uniform: the sales half's four objects (crm_account, crm_contact,
crm_lead, crm_opportunity) and the amount-approval flow are the sales package's, while the
contract-activated hand-off is the revenue package's.

REQ-0003 … REQ-0006 already spell the package-qualified form, so this only brings REQ-0002 into
line with its own siblings.

Why the hand-off citation names two files

src/flows/billing-handoff.flow.ts has no same-named successor — the ADR-0130 move split it
by trigger, so mechanically adding a prefix would have produced a second path naming nothing. The
record cites the file as evidence for the 2026-08-02 ruling that HotCRM models no orders, invoices
or payments, and each half carries that ruling in its own header:

HotCRM owns lead → contract. It does not model Orders, Invoices or Payments (maintainer
decision, 2026-08-02): billing is an external system's job. What the CRM owes that system is a
reliable hand-off, and these two flows are it

So the citation names each half, with what fires it:
src/sales/flows/billing-handoff-closed-won.flow.ts (an opportunity enters closed_won) and
src/revenue/flows/billing-handoff-contract-activated.flow.ts (a contract enters activated).

Verification

pnpm verify — package.json is the single source of truth for that chain and it was run whole,
not narrowed. Run on the final head of this branch, under the shared heavy-verify lock; the exit
code was captured by the lock wrapper, never read through a pipe.

HEAD = ea3735b
os-verify-lock: VERDICT command-exit 0 · held the lock 157s (2m37s) · waited 0s

  ✓ Validation passed (1464ms)            # objectstack validate
  ✓ i18n lint gate: 0 i18n/missing-* issues
  ✓ source hygiene clean
  ✓ source token ratchet clean
  ✓ Build complete (1464ms)               # objectstack build
  Test Files  170 passed (170)
       Tests  3646 passed | 1 skipped (3647)

⚠️ Those seconds are shared-box seconds — the lock excludes other locked runs, not unlocked
sibling work. The verdict, not the duration, is the reading.

Plus a resolution check with both controls, on the working tree: every src/ path the record now
names resolves on disk (4/4), and the two paths it used to name still do not (the checker can
report a miss, so 4/4 is a measurement rather than a vacuous pass).

Changeset

.changeset/req-0002-repoint-the-three-stale-paths.md, empty frontmatter — the sanctioned
"releases nothing" declaration .github/workflows/changeset-check.yml documents, on par with the
skip-changeset label. Rationale: the whole diff is under docs/requirements/ (internal
maintainer documentation) plus the changeset itself. Nothing under src/ or content/docs/ moves,
the package is private: true with no files[], and the built artifact and the published
documentation site are byte-identical to main. This matches the level chosen by the two nearest
precedents — PR #1932 and the commit that created this very record (#1906) both used empty
frontmatter for maintainer-docs-only diffs, while #1921 carried a patch precisely because it
moved content/docs/, the published half.

Acceptance notes

Observations found on the way, none acted on here:


Generated by Claude Code

…the day they were written

`docs/requirements/0002-it-services-project-delivery-and-cost.md` cited three `src/`
paths that no longer exist. #1924 / PR #1932 froze them as a requirements record. The
ancestry says otherwise: the commit that authored the record (9f13c77) is a descendant
of the ADR-0130 layout move (36b27dd), and all three paths are already absent from
`src/` in that very commit's own tree. They never named anything — a typo, not a
true-when-written record, and freezing protects statements that were true when written.

`docs/requirements/README.md` calls a record "the spec an agent starts from", read
"before writing any `*.object.ts`", and PSA is this thread's next step, so an agent
building it from this record is routed into directories that do not exist.

- `src/objects/` → `src/sales/objects/`. The four objects the sales half is judged
  against — `crm_account`, `crm_contact`, `crm_lead`, `crm_opportunity` — are the sales
  package's.
- `src/flows/opportunity-approval.flow.ts` →
  `src/sales/flows/opportunity-approval.flow.ts`.
- `src/flows/billing-handoff.flow.ts` → both successors. The move split that flow in two
  by trigger and there is no same-named successor, so a prefix would still have named
  nothing. The record cites it as evidence for the 2026-08-02 ruling that HotCRM models
  no orders, invoices or payments, and each half carries that ruling in its own header,
  so the record names each half: `src/sales/flows/billing-handoff-closed-won.flow.ts`
  (an opportunity enters `closed_won`) and
  `src/revenue/flows/billing-handoff-contract-activated.flow.ts` (a contract enters
  `activated`).

REQ-0003 … REQ-0006 already spell the package-qualified form, so this brings REQ-0002
into line with its own siblings.

Nothing else in the record changes. `docs/architecture/module-split-plan.md` and
`module-split-inventory.json` are untouched: those describe the pre-move tree on purpose,
which is a different case, and the policy question they raise is the maintainer's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3YsvpK1PvYf9n1YUhYP6W
@vercel

vercel Bot commented Sep 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Sep 15, 2026 7:07pm UTC

Request Review

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 15, 2026
@os-elon-musk
os-elon-musk marked this pull request as ready for review September 15, 2026 19:13
@os-elon-musk
os-elon-musk added this pull request to the merge queue Sep 15, 2026
Merged via the queue into main with commit cb935ca Sep 15, 2026
9 checks passed
os-elon-musk pushed a commit that referenced this pull request Sep 16, 2026
…written

The maintainer ruled the freeze question on 2026-09-16, verbatim and untranslated:

> 写时为真的陈述冻结;写下时就已失效的路径按错误改正。这条写进 `docs/requirements/README`,以后不再逐卡吵。

`docs/requirements/README.md` gains one section that writes it down as a criterion
rather than a conclusion, so the next reader can decide a record nobody has argued
about yet. The criterion is two timestamps: the commit that wrote the sentence,
against the commit that moved, renamed or deleted the structure it cites. Earlier
means the sentence was true when written and is frozen; later means it named nothing
the day it was typed, which is a typo to correct.

The measurement is given in the cheap, shallow-safe form — a lookup in the authoring
commit's own tree rather than an ancestry walk — with the shallow-checkout trap that
makes `--diff-filter=A` read like a real answer called out beside it.

The "why" hangs on this file's own opening, which already defines these records as
"the human-or-customer input an AI agent reads before authoring metadata": a path
that resolved to nothing the day it was written has no fact underneath it, so
freezing it protects nothing and routes the next agent into a directory that does
not exist.

Both worked examples are already in this directory and come out opposite ways, which
is what makes the criterion testable rather than decorative: REQ-0001 is frozen (its
commits predate the ADR-0130 move, and the directory it names is in the tree at its
own commit), REQ-0002 was corrected in PR #1942 (its commit lands 29 minutes after
that move, and none of the three paths it cited are in the tree at that commit).

No requirement record changes, the template is untouched, and the section states that
it does not decide the document class that describes a superseded tree on purpose.

Claude-Session: https://claude.ai/code/session_01T3YsvpK1PvYf9n1YUhYP6W

Co-authored-by: Claude <noreply@anthropic.com>
os-elon-musk pushed a commit that referenced this pull request Sep 16, 2026
…written

The maintainer ruled the freeze question on 2026-09-16. The wording below is that
ruling as adopted rather than as typed — the PM put it to the maintainer, who adopted
it with 「其他同意」 ("agreed to the rest") — so it is quoted exactly and kept
untranslated, because the adopted words are the ruling.

> 写时为真的陈述冻结;写下时就已失效的路径按错误改正。这条写进 `docs/requirements/README`,以后不再逐卡吵。

`docs/requirements/README.md` gains one section that writes it down as a criterion
rather than a conclusion, so the next reader can decide a record nobody has argued
about yet. The criterion is two timestamps: the commit that wrote the sentence,
against the commit that moved, renamed or deleted the structure it cites. Earlier
means the sentence was true when written and is frozen; later means it named nothing
the day it was typed, which is a typo to correct.

The measurement is given in the cheap, shallow-safe form — a lookup in the authoring
commit's own tree rather than an ancestry walk — with the shallow-checkout trap that
makes `--diff-filter=A` read like a real answer called out beside it.

The "why" hangs on this file's own opening, which already defines these records as
"the human-or-customer input an AI agent reads before authoring metadata": a path
that resolved to nothing the day it was written has no fact underneath it, so
freezing it protects nothing and routes the next agent into a directory that does
not exist.

Both worked examples are already in this directory and come out opposite ways, which
is what makes the criterion testable rather than decorative: REQ-0001 is frozen (its
commits predate the ADR-0130 move, and the directory it names is in the tree at its
own commit), REQ-0002 was corrected in PR #1942 (its commit lands 29 minutes after
that move, and none of the three paths it cited are in the tree at that commit).

No requirement record changes, the template is untouched, and the section states that
it does not decide the document class that describes a superseded tree on purpose.

Claude-Session: https://claude.ai/code/session_01T3YsvpK1PvYf9n1YUhYP6W

Co-authored-by: Claude <noreply@anthropic.com>
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants