fix review findings: CI leaf-gate wiring, heritage return surface, AGENTS.md self-containedness

- run-gates.ts docSyncLeafGates() gains verify-export-jsdoc — CI lanes
  and the pre-push hook execute this leaf list, not the doc-sync npm
  script, so the gate was previously unenforced there (proven by
  SessionForkErrorCode landing undocumented via a master merge while
  checks stayed green; now documented). Same wiring gap fixed for
  master's verify-config-catalog, which was also missing from the list.
- The heritage exemption now recovers the base's return surface: a void
  base return carried no @returns duty, so an override returning a
  concrete result documents it itself (annotated overrides run the
  standard check; unannotated ones are classified by the checker so
  faithful void overrides need no boilerplate annotation). Three new
  negative-path tests pin it; RFC and module doc updated.
- AGENTS.md states each principle inline instead of citing RFCs (eight
  citations removed; high-level doc links kept) and the editing section
  now carries the self-containedness rule.
- Generated catalogs/graphs regenerated for the shifted line pointers.
This commit is contained in:
Tianyi Cui
2026-07-07 20:30:34 +08:00
parent 9296411ea1
commit 5e4ac5e472
7 changed files with 148 additions and 31 deletions

View File

@@ -487,4 +487,69 @@ export class Impl extends Base {
}
`))).toEqual([expect.stringMatching(/exported class method 'Impl.run' .* is a binding pattern/)])
})
it('revives the @returns duty when an override grows a concrete result over a void base', () => {
const voidBase = `
/** Seam. */
export abstract class Base {
/** Do it (fire-and-forget). */
abstract run(): void
}
`
expect(collectExportJsdocViolations(make(`${voidBase}
/** Impl. */
export class Impl extends Base {
override run(): number { return 1 }
}
`))).toEqual([expect.stringMatching(/exported class method 'Impl.run' .* is missing @returns \(return type: number\)\./)])
expect(collectExportJsdocViolations(make(`${voidBase}
/** Impl. */
export class Impl extends Base {
/**
* Do it and count.
* @returns how many were done.
*/
override run(): number { return 1 }
}
`))).toEqual([])
})
it('classifies an unannotated override return over a void base via the checker', () => {
const voidBase = `
/** Seam. */
export abstract class Base {
/** Do it (fire-and-forget). */
abstract run(): void
}
`
expect(collectExportJsdocViolations(make(`${voidBase}
/** Impl. */
export class Impl extends Base {
override run() { return 1 }
}
`))).toEqual([expect.stringMatching(/exported class method 'Impl.run' .* non-void result its heritage declaration does not document/)])
expect(collectExportJsdocViolations(make(`${voidBase}
/** Impl (faithful void, no annotation needed). */
export class Impl extends Base {
override run() {}
}
`))).toEqual([])
})
it('keeps the full exemption when the base return already carries the @returns duty', () => {
expect(collectExportJsdocViolations(make(`
/** Seam. */
export abstract class Base {
/**
* Count things.
* @returns the count.
*/
abstract run(): number
}
/** Impl. */
export class Impl extends Base {
override run(): number { return 1 }
}
`))).toEqual([])
})
})

View File

@@ -373,6 +373,14 @@ export class Session {
/** A fork source: either the live session object or its live store id. */
export type SessionForkSource = Session | SessionId
/**
* Rejection codes for session forking: the fork source id is unknown to the
* live store (`SESSION_NOT_FOUND`) or names a session object that is not the
* store's live instance (`SESSION_NOT_LIVE`); the requested child id is
* already taken (`SESSION_ALREADY_EXISTS`); the boundary is not a contiguous
* existing seq (`INVALID_BOUNDARY`); or the boundary event is not a
* `turn/end` — a fork must cut on a closed turn (`OPEN_TURN`).
*/
export type SessionForkErrorCode =
| 'SESSION_NOT_FOUND'
| 'SESSION_NOT_LIVE'