Commit Graph
1019 Commits
Author SHA1 Message Date
Yichen Jiang e97fffeab7 refactor(agent): rename agent/request-messages to agent/request-advice
tianyicui's review: the seam name did not say what the event or its
types do. 'advice' reads both ways — advisory content for the model,
and AOP before/after advice woven around a join point (here the
derived history) without modifying it — so RequestAdvice.before/after
are self-describing. Types follow: RequestAdvice / RequestAdviceContext;
the logged EpochHeader fields keep their positional names
(messagePrefix/messageSuffix).

Also sharpens the core.md wording the review flagged as ambiguous:
before-advice sits in front of the ENTIRE derived history, directly
after the system slot (the conventional home for session-stable openers
— an AGENTS.md digest, a skills catalog), after-advice follows the
history's last message. Catalogs and doc graphs regenerated.
2026-07-08 10:05:34 +08:00
Yichen Jiang 731ae2443c Merge remote-tracking branch 'origin/master' into agent-request-messages
# Conflicts:
#	docs/cordis-catalog/events.md
#	docs/event-producer-consumer.md
#	docs/persistence-catalog.md
2026-07-07 21:03:07 +08:00
Tianyi Cui 5baacf5fd3 Merge pull request #190 from deepseek-harness/worktree-export-jsdoc-gate
Gate JSDoc completeness on every package export
2026-07-07 20:57:01 +08:00
Yichen Jiang f3d26ed049 fix request-messages boundary immutability 2026-07-07 20:55:13 +08:00
Tianyi Cui 5e4ac5e472 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.
2026-07-07 20:30:34 +08:00
Yichen Jiang 17bd71e530 feat(agent): add the agent/request-messages request-only message seam
A new waterfall near request construction lets plugins contribute
request-ONLY messages framing the derived history: RequestMessages
{ before, after } with a frozen empty seed, fired inside the open step
after the agent/request config waterfall, so the step/start boundary
snapshot and its same-sync-frame invariant are untouched. The request
becomes messagePrefix + boundary snapshot + messageSuffix.

Contributions never enter session history — deriveMessages() is
unchanged — so the request header is their durable record:
EpochHeader gains messagePrefix/messageSuffix (canonical absence for
empty arrays), request/header-delta replaces either array whole with
an empty array encoding the transition back to absence, and the
dev-mode reconstruction cross-check now expects the folded header's
framing around the boundary derivation.

This is the seam for per-request advisory context that must be
model-visible now without becoming durable history (a skills catalog,
an environment reminder), keeping the base system prompt
workspace-independent and provider prefix caches stable. The docs
carry the channel cost model: session-frozen content belongs in
before, low-frequency change notices belong in durable history via
inject() (paid once, prefix-cached thereafter), and after is reserved
for small frequently-refreshed state snapshots re-paid on every
request they ride. No shipped producer yet, so ACP snapshot fixtures
are byte-identical.
2026-07-07 19:42:30 +08:00
Tianyi Cui 9296411ea1 Merge remote-tracking branch 'origin/master' into worktree-export-jsdoc-gate
# Conflicts:
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
2026-07-07 17:17:03 +08:00
Tianyi Cui 51b715433d fix review finding: an export list surfaces only the declarators it names
A name resolved through an export list (or a default-export identifier)
mapped back to its whole VariableStatement, and checkDecl walked every
declarator — so a private sibling sharing the statement with an exported
const was wrongly required to carry JSDoc.

The scope dispatch is now two-phase: requests accumulate per statement
(null = whole statement for a direct export modifier or ambient scope;
name sets union across lists, so two lists naming different declarators
of one statement both count), then each surfaced statement is checked
once with the declarator filter. Regressions pin the private-sibling
skip, the cross-list union, and the default-export sibling.
2026-07-07 17:05:32 +08:00
Tianyi Cui e477d76199 Merge pull request #119 from deepseek-harness/session-fork
Add session fork service
2026-07-07 16:58:07 +08:00
Tianyi Cui 3c572cf70a fix CI: stop fixture programs parsing the default lib (spec timeouts)
The coverage CI lane timed out (5000ms/test) in verify-export-jsdoc.spec.ts:
every test builds a ts.Program, and the fixture branch of
loadCompilerOptions omitted noLib, so each of the 34 tests parsed the
full default lib — measured 231ms/program bare, ~1.5s/test under
coverage instrumentation locally, past 5s on a 2-core runner. Fixtures
are self-contained and nothing in the walk resolves a lib symbol:
noLib + types:[] drops program creation to ~1ms and the spec's
coverage-mode test time from 53s to 0.16s. The real-repo branch
(tsconfig.base.json options) is untouched.
2026-07-07 16:22:43 +08:00
Tianyi Cui 6d6b554fdf Merge remote-tracking branch 'origin/master' into worktree-export-jsdoc-gate
# Conflicts:
#	docs/cordis-catalog/services.md
#	docs/persistence-catalog.md
#	docs/rfc/INDEX.md
#	package.json
#	scripts/gen-cordis-catalog.ts
2026-07-07 09:34:12 +08:00
Hypatia May dad928e83b docs: refresh config catalog source link 2026-07-07 09:22:56 +08:00
Hypatia May 05acc6ebc3 Merge remote-tracking branch 'origin/master' into session-fork 2026-07-07 09:21:01 +08:00
Hypatia May 3e44860544 docs: refresh config catalog source links 2026-07-07 09:19:59 +08:00
Tianyi Cui dc5656fab3 Merge pull request #194 from deepseek-harness/worktree-snapshot-pin-request-header
Snapshot tests: pin request-header content in one scenario, tokenize the rest
2026-07-07 09:18:58 +08:00
Hypatia May 5050bc30c7 Merge remote-tracking branch 'origin/master' into session-fork 2026-07-07 09:18:17 +08:00
Tianyi Cui 17f89642ae Merge branch 'master' into worktree-snapshot-pin-request-header 2026-07-07 09:16:28 +08:00
Tianyi Cui 09dbc06079 Merge pull request #193 from deepseek-harness/worktree-hooks-dispose-quiescence
fix(hooks): drain detached hook runs on bridge dispose
2026-07-07 09:15:31 +08:00
Tianyi Cui 2d3f75c77b Merge branch 'master' into worktree-hooks-dispose-quiescence 2026-07-07 09:13:00 +08:00
Tianyi Cui 25fc167165 Merge branch 'master' into worktree-snapshot-pin-request-header 2026-07-07 09:09:27 +08:00
Hypatia May 4f55c15cf8 Merge remote-tracking branch 'origin/master' into session-fork 2026-07-07 09:06:00 +08:00
Hypatia May c7fba41ba9 Inline session fork parameters 2026-07-07 09:04:49 +08:00
Tianyi Cui 515d04339b Address review round 2: preserve delta insert arity; reject unpinned header-deltas
Residuals from the Codex re-review:

1. A system delta's insert was flattened to one token, so deltas differing
   only in inserted-line count compared equal. Now one {{system}} token per
   inserted line — position AND extent survive, content does not.

2. The live uniformity guard folded only request/header snapshots, so a
   mid-run header CHANGE (request/header-delta) could diverge from the pin
   invisibly. Non-pinning runs now assert zero header-delta events: a
   scenario that legitimately changes its header mid-run exists to show
   that change, so it must pin (fail-loud until it does).
2026-07-07 01:26:25 +08:00
Tianyi Cui a0d8f33b29 Address review: structure-preserving delta scrub, live header-uniformity guard, RFC style
Codex review findings on the pinned-header change:

1. scrubRequestHeaders flattened a request/header-delta's whole
   system/tools payload to one token, so two meaningfully different deltas
   compared equal. Now the structural facts survive — keepStart/keepEnd
   line positions, added/removed/changed tool NAMES — and only the bulk
   (inserted prompt lines, schema bodies) is tokenized.

2. The one-pin design rested on an unasserted premise (all sessions
   compose the same header). Every non-pinning scenario now asserts, live,
   that each request/header its run produces equals the pinned fixture's
   header (both sides normalized against their own volatile values), so a
   session-dependent header fails loud until it gets its own pin.
   Verified the guard bites: perturbing the pinned fixture's prompt fails
   a non-pinned scenario with the intended message.

3. RFC de-slopped per docs/AGENTS.md: no PR reference, no SHOULD
   spec-speak; Decision/Verification/Consequences updated for 1 and 2.
2026-07-07 00:51:55 +08:00
Tianyi Cui 81cbc3daa4 Merge pull request #191 from deepseek-harness/catalog-flatten
docs: flatten single-file catalog dirs to docs/tool-catalog.md and docs/persistence-catalog.md
2026-07-07 00:30:36 +08:00
Tianyi Cui 0c568201c9 Merge remote-tracking branch 'origin/master' into catalog-flatten 2026-07-07 00:29:12 +08:00
Tianyi Cui 215ff9a4e6 Merge pull request #188 from deepseek-harness/config-catalog
feat: generated plugin config catalog (docs/config-catalog.md)
2026-07-07 00:27:59 +08:00
Tianyi Cui f7bd7e82d1 fix review findings: restrict export-import aliases to prose-only targets
Codex round-3: alias prose matches the gate's strength only when the
target's own contract is prose-only. An export-import alias to a
function, class, or namespace target (or an unresolvable one) is now
refused — those carry signature/member contracts the alias cannot
hold; export the declaration directly instead. Const/enum/interface/
type-alias targets keep the self-documentation contract.

Tests pin the refusal for function, class, and namespace targets;
module doc and RFC updated.
2026-07-07 00:20:55 +08:00
Tianyi Cui d45e470192 fix: retarget the i18n pairing exclusions to the flattened catalog paths
The manifest excluded docs/tool-catalog/ and docs/persistence-catalog/ as
directories; the trailing slash is a deliberate path boundary, so the
flattened single files no longer matched and lost their generated-doc
classification — the gate would have accepted a hand-added .zh.md twin it
must reject. The entries now name the files.
2026-07-07 00:08:55 +08:00
Tianyi Cui 4e2da27201 Merge branch 'config-catalog' into catalog-flatten 2026-07-07 00:06:19 +08:00
Tianyi Cui a4152705ef fix review finding: abort tests must prove the hook process is gone
Codex review of the draft: the abort-on-dispose tests asserted only that
dispose() resolved and no warning fired — a regression back to untracked
fire-and-forget runs would pass both. The hook script now records its
shell PID before sleeping, and after dispose the test asserts
process.kill(pid, 0) throws: the drain resolves only after the killed
run settled (the child reaped), so ESRCH-by-dispose-return is
deterministic under the correct implementation, while an alive or
unreaped process fails it. Verified the discriminator by gutting drain()
locally: exactly these two tests fail.
2026-07-07 00:02:10 +08:00
Tianyi Cui f0ef280848 fix: identity-check name resolutions across the pasted closure; exclude the catalog from i18n pairing
Review findings on the config catalog:

The transitive-paste closure deduplicated by bare name, so two DIFFERENT
package-local declarations sharing a name would silently collapse to the
first one pasted — and a reference satisfied by the wrong declaration is a
wrong catalog, not just a thin one. Every resolution is now identity-checked
by source pointer: reaching the same declaration again is benign, while a
name that resolves to two distinct declarations, to conflicting imports, or
to a declaration in one file and an import in another is a violation — a
verbatim fence has a single flat namespace, so the fix is a rename at the
source. A spec case pins the two-declarations collision.

docs/config-catalog.md also joins the translation-pairing exclusions: it is
a generated English-only doc, and without the entry the gate would accept a
hand-added .zh.md twin it must reject.
2026-07-07 00:00:34 +08:00
Tianyi Cui eaac3b58a4 fix review findings: close wrapped-expression, alias, and binding-pattern gaps
Codex round-2 review found three adjacent fail-open shapes:

- Wrapped function expressions escaped classification: parentheses,
  as/satisfies casts, and non-null assertions are now peeled before the
  arrow/function test (initializers and default exports), and a
  single-call-signature type literal counts as the surface signature.
  A literal mixing call/construct signatures with anything else is
  refused outright — no single signature to hold the tags against.
- The blanket 'export import X = N.member' skip was unsound (the target
  can be a non-exported namespace member no walk visits): an alias now
  documents itself.
- The heritage extra-parameter duty missed binding-pattern extras,
  which no base declaration can name: they now trigger the standard
  binding-pattern violation.

Five new negative-path tests pin the closed shapes; module doc and RFC
updated.
2026-07-06 23:55:16 +08:00
Tianyi Cui 1fd9e2a9ab Pin request-header content in one snapshot scenario
Every session.jsonl fixture embedded the full composed system prompt and
complete tool-schema list in its request/header event (~8 KB on one line,
identical across the suite), so any prompt or tool-schema edit forced a
re-record or hand-edit of every fixture — see the dynamic-workflows PR for
the churn pattern this removes.

Now exactly one scenario (text-turn, flagged pinsHeader) commits and
compares that content verbatim; every other fixture stores and compares it
as {{system}}/{{tools}} tokens via the new pure scrubRequestHeaders
normalizer (applied to both compare sides and to record-mode writes, so a
re-record cannot reintroduce the content). request/header-delta payloads
are scrubbed the same way; config/reason stay verbatim — a model swap
SHOULD churn every fixture, a prompt edit should not. Replay is unaffected:
script derivation reads only assistant/chunk events.

Fixture meta-guards enforce the split: non-pinning fixtures must be fixed
points of the scrub, the pinning fixture must not be, and exactly one
scenario pins. Committed fixtures migrated through the same function.

Docs: pinned-header RFC (implemented/testing), base snapshot RFC + testing
policy + llm-replay module doc/README updated.
2026-07-06 23:41:20 +08:00
Tianyi Cui 82dfc62620 Merge branch 'config-catalog' into catalog-flatten 2026-07-06 23:38:40 +08:00
Tianyi Cui fe815c9586 Merge remote-tracking branch 'origin/master' into config-catalog
# Conflicts:
#	docs/rfc/INDEX.md
2026-07-06 23:31:58 +08:00
Tianyi Cui 9ca31ab193 fix(hooks): drain detached hook runs on bridge dispose
The emit-shaped hook points (SessionStart, SubagentStart, SubagentStop)
run fire-and-forget: no seam awaits the run chain, so disposing a bridge
could strand a live hook process and let a late continuation inject into
a disposed context. The floating continuation also made the coverage
gate racy: the only coverage of the SubagentStart continuation's
no-context branch arm rode on an un-awaited .then, and on a loaded CI
runner the fork's per-file coverage snapshot beat it — master run
28798191671 failed the 100% branch gate on hooks-claude/src/index.ts at
99.03% (uncovered line 336) with the identical tree passing the PR run
three minutes earlier.

New shared primitive createDetachedRuns() in dsh-hook-protocol: a bridge
tracks each detached run chain, passes the tracker's abort signal to
runHook, and registers drain() as its effect disposer — drain aborts
still-running hook processes (a kill via the bash seam, not a wait out
to the 10-minute default hook timeout), then resolves once every chain
has settled. fiber.dispose() resolving now means the bridge's detached
work is quiescent (docs/defensive-patterns.md).

The subagent marker test disposes the bridge as its sync point, so the
formerly racy branch arm is executed deterministically before the file's
coverage snapshot; new tests pin abort-on-dispose promptness for both
bridges and the tracker's settle/drain contract in hook-protocol.
2026-07-06 23:27:54 +08:00
Tianyi Cui 9ff010cbef fix review findings: close export-form and heritage-exemption gaps
Codex round-1 review found three fail-open paths in the new gate:

- Unhandled export forms passed silently. checkDecl now fails CLOSED on
  unrecognized exported statement kinds, 'export =' is refused outright,
  'export import X = N.member' is an explicit documented skip (alias;
  definition site owns the doc), and ambient 'declare namespace' bodies
  recurse with implicit export semantics.
- Function-like exports escaped the function contract: non-identifier
  default exports and consts with INLINE function-type annotations now
  get full @param/@returns checks (the named-type waiver stays for
  reference annotations only).
- The heritage exemption was name-only: it no longer exempts a public
  override of a protected-only base member, and parameters the base
  never names keep their @param duty (underscore-prefixed renames of a
  base parameter count as the same parameter).

Eight new negative-path tests pin the closed gaps; RFC and module doc
updated to the refined contract.
2026-07-06 23:25:33 +08:00
Tianyi Cui 6d9a4f1258 Merge branch 'config-catalog' into catalog-flatten
# Conflicts:
#	docs/config-catalog.md
#	scripts/gen-config-catalog.ts
2026-07-06 23:16:29 +08:00
Tianyi Cui fb3f3a69ec fix: extend the schema cross-check to nested key paths; scope the catalog framing
Review findings on the config catalog:

The schema-subset check compared only top-level z.object keys against
top-level type members, so a nested loader-accepted key (agents[].id,
agentOptions.model, capabilities.*) missing from the declared type would
pass the gate unseen. The walk now collects nested object/array
compositions as key paths and resolves each against the declared config
type — through interfaces (heritage included), aliases, literals,
intersections, unions, arrays, indexed access, Partial-style wrappers, and
type references across package-local and workspace imports (re-export
chains included). The check stays presence-only and one-directional, and
only a definite miss is a violation: a path crossing a type the walk
cannot enumerate (an external package's) is skipped, never mis-reported.
The recursion guard applies at named declarations only — a structural
first child shares its span start with its parent, so a span-keyed guard
on every node mistakes ordinary descent for a cycle and silently turns
definite misses into unknowns.

The page and RFC framing also overstated the catalog as the exact
cordis.yml-settable surface: the paste is the plugin's full declared
config type, and a field the runtime schema deliberately excludes (the ACP
bridge's test-injected stream) is a runtime-only seam its own JSDoc marks.
Both now say so.

Five spec cases pin the new behavior: nested hidden key, workspace
intersection via star re-export, Partial wrapper, indexed-access
composition, and the external-type unknown path.
2026-07-06 23:06:03 +08:00
Tianyi Cui 116e2c45a8 docs: flatten single-file catalog dirs to docs/tool-catalog.md and docs/persistence-catalog.md
A directory holding exactly one file adds a path level for nothing;
cordis-catalog/ keeps its folder because it holds two sibling pages
(events.md + services.md), matching config-catalog.md which was born flat.

docs/tool-catalog/tools.md -> docs/tool-catalog.md and
docs/persistence-catalog/log-events.md -> docs/persistence-catalog.md; their
generators' OUT paths and in-page relative links drop one directory level,
and every citation repo-wide (docs, RFCs, package READMEs, generator
headers) is updated. graph-atlas.md, config-catalog.md, and the doc graphs
regenerate with the new paths.
2026-07-06 22:26:06 +08:00
Tianyi Cui 54bb9e5ec7 Merge remote-tracking branch 'origin/master' into worktree-export-jsdoc-gate
# Conflicts:
#	docs/development.i18n.yaml
#	docs/rfc/INDEX.md
2026-07-06 22:25:39 +08:00
Tianyi Cui 0606cd559c Merge pull request #175 from deepseek-harness/codex/parallelize-ci
ci: parallelize GitHub workflows
2026-07-06 22:15:13 +08:00
Tianyi Cui 8458530298 Merge branch 'master' into codex/parallelize-ci 2026-07-06 22:12:44 +08:00
Wenlu Wang 654d192c47 Merge pull request #182 from deepseek-harness/codex/gate-cadence-docs
docs: clarify local gate cadence
2026-07-06 22:10:19 +08:00
Tianyi Cui cd9737d569 Gate JSDoc completeness on every package export
New doc-sync gate verify-export-jsdoc walks every module-level exported
name under packages/*/*/src and requires description prose everywhere,
plus @param per parameter and @returns on non-void annotated returns for
function-like exports, public class methods, properties, and accessors.
The parsing + check helpers move out of gen-cordis-catalog.ts into a
shared scripts/jsdoc.ts so 'documented' means one thing on both gated
surfaces.

Deliberate exemptions (documented in the RFC): heritage-declared class
members (the seam declaration is the doc's one home — the one checker
query in an otherwise pure-AST walk), cordis plugin-protocol slots
(name/inject/reusable/Config/apply, top-level and static), constructors,
overload implementations, declare-module augmentation bodies, and
re-export statements (checked at the defining module).

The 203 under-documented exports the gate found at adoption are filled
in this change, so the gate lands green; generated catalogs/graphs are
regenerated for the shifted line pointers.

RFC: docs/rfc/implemented/process/2026-07-06-export-surface-jsdoc-gate.md
2026-07-06 22:09:30 +08:00
Tianyi Cui b0c7eadd23 feat: generated plugin config catalog (docs/config-catalog.md)
scripts/gen-config-catalog.ts walks every packages/<group>/<pkg> entry with
the TypeScript compiler API and emits docs/config-catalog.md: per loadable
plugin, the verbatim config declaration (JSDoc included) its apply/constructor
receives in a ts config-catalog fence, the inject requirements, resolved links
for every referenced type (package-local types pasted transitively, other
plugins' config types as intra-page anchors, LINK_MAP names to
core-data-structures, workspace types to source), and terse classification
lists for config-free plugins, abstract seams, and libraries — classification
is total, so a new package cannot go undocumented.

The walk enforces per-field JSDoc prose on every pasted declaration and
statically cross-checks the schemastery schema (z.object keys, z.intersect
composition across packages): every schema-validated key must be a declared
member of the config type. One violation existed repo-wide — the agents[].id
field in dsh-agent-loop — fixed by adding its JSDoc (which shifts the
cordis-catalog services page's source pointers; regenerated).

verify-config-catalog (--check) joins doc-sync; doc-typecheck learns the
ts config-catalog fence; gen-cordis-catalog exports its JSDoc/pointer helpers
and LINK_MAP for reuse. Negative-path spec in
packages/core/agent-core/tests/gen-config-catalog.spec.ts mirrors the
gen-cordis-catalog spec. Decision record:
docs/rfc/implemented/process/2026-07-06-generated-config-catalog.md (includes
the deliberate acceptance of README ## Config overlap).
2026-07-06 21:57:17 +08:00
Tianyi Cui 9df79534a6 Merge branch 'master' into codex/gate-cadence-docs 2026-07-06 21:55:48 +08:00
Tianyi Cui c57c7527a5 Merge branch 'master' into codex/parallelize-ci 2026-07-06 21:54:07 +08:00
Tianyi Cui 1c999804d8 Merge pull request #184 from deepseek-harness/codex/clarify-doc-budget-guidance
Clarify doc budget guidance
2026-07-06 20:46:31 +08:00