Commit Graph
5 Commits
Author SHA1 Message Date
a1chzt 41027b6ccf Merge main into share-comment integration preserving motion and share lifetimes 2026-10-16 13:30:48 +08:00
CaprikaandGitHub c27fca2723 fix(comments): persist new static comment identities (OPEND-3516) (#8768)
* fix(comments): preserve authored identities through manual and AI edits

* fix(comment): prepare durable static source identities (OPEND-3516)

* fix(comments): integrate durable identity lifecycle (OPEND-3516)

* fix(comment): validate authored HTML identity tokens (OPEND-3516)

* fix(comment): mask raw text before authored tokenization (OPEND-3516)

* fix(comments): enforce prepared document and publication barriers (OPEND-3516)

* fix(comments): revalidate pending documents and guard updates (OPEND-3516)

* fix(comments): require app-owned source preparation consent (OPEND-3516)

* fix(comments): contain preparation consent focus (OPEND-3516)

* fix(sharing): diagnose project bootstrap failures safely (OPEND-3516)

* fix(comments): retire stale publication guard alerts on explicit retry (OPEND-3516)

* fix(comments): locate Personal identities in the live preview (OPEND-3516)

* fix(comments): surface lost Owner identity anchors (OPEND-3516)

* fix(comments): unblock publication after strict target loss
2026-10-15 19:10:14 +08:00
open-design-crew[bot]GitHubbone3deep1962-collabWilliamopen-design-crew[bot] <4197780+open-design-crew[bot]@users.noreply.github.com>NJUHuaAmy
14fee234e6 feat: add motion design workflow and curated motion skills (#8619)
* feat: add Motion Design skill and OD Next task route

* fix(web): rename motion design entry and place it second

* fix(web): reconcile motion design catalog consumers

* feat(web): add motion design templates to Home and Community

Five motion design example templates (3D procedural island, explainer,
product launch, showreel, data story), chosen by community reach, under the
Home motion-design chip and a new Community "Motion design" type.

Each template follows the bundled example layout: an OD-generated
example.html with its runtime libraries under assets/, an HTML preview baked
by the existing preview pipeline, and a SKILL.md that carries example.html
as the reference when the card is picked on the OD Next motion-design route.
Home cards ship curated copy for every UI locale. Motion templates stay off
the Video and HyperFrames chips.

* fix(skills): keep motion-design procedural layers visible in video export

The MP4 exporter seeks timelines with callbacks suppressed
(renderSeek(t, { suppressEvents: true })), so content drawn only from a GSAP
onUpdate callback is missing from exported video. Recommend a per-frame draw
loop that reads the timeline clock, and state that visible content must not
depend on timeline callbacks. The OD Next task asset is re-synced with
scripts/sync-motion-design-skill.ts.

* fix(motion): preserve reference assets and exported animation frames

* feat(skills): integrate curated motion helpers with complete runtime files

* feat(skills): add Huashu, Oil Motion and Motion.Lab adapters

* test: classify animate design guidance in narration guard

* feat(motion): refine five community templates and portable previews

* fix(motion): refine launch transitions and center gallery previews

* fix: allow free-form briefs on automatic motion task route

* feat(motion): add template soundtracks and audio export

* fix(motion): require rendered MP4 alongside editable video source

* test(motion): align strategy activation expectation with package version

* fix: scope motion delivery and make editable sources playable

* fix(motion): bundle source dependencies and valid closing protocol

* feat(motion): rebuild the five templates on the most-viewed community posts

Re-select topics by single-post views in the latest awesome-opus-5.5-video
snapshot and anchor each template to the top eligible post of its genre:
camera-focus explainer, showreel, product launch, procedural island and a
new wordless animated short that replaces the data story.

All five example.html files are hand-authored canvas/Three.js films drawn by
one render(t) from the GSAP master timeline, so preview, scrubbing and MP4
export produce identical frames. Each ships an original procedural
soundtrack, updated SKILL.md and 19-locale copy; Home order follows reach.

* fix(plugins): load bundled fonts in plugin previews

Plugin previews blocked every bundled font: the preview CSP had no font-src,
and relative url() references inside inline <style> blocks kept pointing at
the preview URL instead of the plugin folder. Allow same-origin fonts, map
inline-style url() references through the existing asset route (escapes,
absolute and data: URLs stay untouched), and serve font files with font
MIME types.

* feat(community): make motion template Use match the Home example pick

Community 「做同款」 on a motion design template now binds it as that type's
own example card, carrying the type's project metadata, so the run takes the
same OD Next motion-design route as a pick from Home's motion design list and
delivers the MP4 together with the editable source. Other task types keep
the existing plugin-pinning hand-off.

Community use clicks now record use_source (card | detail_modal) and
use_action (use | use_with_query); the details modal Use is tracked for the
first time. The Home hand-off no longer records a second community_gallery
click for uses Community already tracked.

* fix(plugins): count the five motion templates in the official registry metadata

* fix(media): keep HyperFrames audio filters compatible with FFmpeg 4.1

CI's bundled Linux FFmpeg 4.1 rejects adelay all=1 and amix normalize=0.
Make each track stereo and delay both channels explicitly, skip amix for a
single track, and undo amix averaging with a gain for several tracks.

* fix(motion): keep template previews bakeable on slow CI renderers

CI's software-GL baker skipped the 3D island (preview readiness timeout):
under slow frames GSAP lag smoothing slowed the timeline, so the 8 s
poster frame never arrived within the baker's 15 s wait. Play every
template on the wall clock and move the island poster to 6 s. With
simulated 300 ms frames all five now report ready within 10 s.

* feat(motion): add the kinetic typography template

Template selection now follows the motion design spec's four content types
(brand and product films, kinetic typography, explainer animation, procedural
art). Kinetic typography takes the fifth slot, replacing the animated short.
The new template is a 15-second, 120 BPM type film with an original
soundtrack and 19-locale copy.

* fix(motion): let the project motion player fit template stages

Inside a project the motion source player wraps the composition, fits it and
adds its own controls. The templates also scaled their stage, so editable
sources from Remix and Community use rendered at a fraction of the preview.
Templates now keep their own fit and controls only outside that player.

* feat(motion): restage the 3D island as a midday lagoon

The island moves from golden hour to a bright tropical midday closer to its
anchor post: a shader lagoon coloured by a baked depth map with ripples,
shallow-water shimmer and shore foam, a wide white-sand beach, palms and
instanced bushes, a village with a pier, soft sun shadows and a lower
camera path. The CPU wave mesh is gone, so frames cost no more than before.
Copy is updated in all 19 locales.

* fix(motion): keep each soundtrack inside the composition root

A Community use regenerated the island source tail and dropped the root
<audio> element, so its MP4 had no sound. Templates now place the element
inside the composition root next to the stage.

* docs(motion): state the template selection rule as complete works

* fix(motion): enforce video clocks and bound content verification

* feat(motion): replace the motion templates with remakes of viral posts

The five Motion design templates are now frame-faithful remakes of viral
motion posts, so each preview shows the look users came for: the 15-second
motion designer showreel, a high-energy motion reel (copy set in English), a
one-shape UI motion piece, a pixel-art wizard and a 20-second electric-blue
tech reel. Each ships open-licensed fonts, an original soundtrack, the
creator's published prompt where one exists, and credits its source post.

The explainer, product launch, 3D island and kinetic type templates are
removed. Tests follow the new set; a backward-seek check now covers hidden
WebGL layers.

* feat(motion): feature The Future Is Already Here first and start preview sound with the film

* chore: ensure PR CI

* fix(motion): match The Future Is Already Here controls and reorder the motion templates

* fix(motion): make the one-shape UI preview controls legible

* fix(motion): hide The Future Is Already Here controls inside the project player

* fix(motion): plan suitable sound and synchronize source audio playback

* feat(motion): keep remake soundtracks by default and fade exported audio where it stops

* test(motion): cover audible source controls in Chromium

* fix(test): use explicit ESM imports in audio player regression

* fix(motion): align preview track discovery with video export

* feat(motion): autoplay looping HTML without a player bar

* fix(test): consume the built motion playback contract

* docs(motion): state explicit soundtrack rules for make-same and the music line in replies

* chore(motion): version the audible looping delivery profile

* docs(motion): let template sound rules lead the general policy and freeze the audio contract

* fix(motion): scope autoplay to declared video motion sources

* fix(motion): reject audible MP4s under an explicit silent contract

* docs(motion): drop the routine music note from make-same replies

* test(motion): align delivery regressions with looping source playback

* fix(motion): isolate preview and audio export from legacy HyperFrames

* test(motion): retain the existing desktop legacy audio limitation

* fix(motion): suspend audio when previews leave the active workspace

* feat(motion): replace two motion templates with a product promo and a visual lesson; sign the showreel as Open Design

* feat(motion): give the showreel the Open Design palette and a full-size stacked wordmark

* feat(motion): give the product promo a livelier mascot girl

* refactor(motion): rebuild The Future Is Already Here as readable, editable scene code

* fix(motion): pick gallery cover frames that survive the baker's capture delay

* feat(motion): sharpen the showreel opener, depth, truchet and end card

* feat(motion): make the product promo mascot talk, add a speaking score and the flow pop

* feat(motion): give the visual lesson the original's paper-collage texture

* fix(motion): bundle gallery covers for fresh branch checkouts

* feat(motion): feature and refine Bring Ideas to Life

Adapt the paper story around OpenDesign capabilities, with short rounded hands, clearer timing, continuous paper actions, and a larger audience invitation. Feature it first in Motion and synchronize localized names, metadata, cover, guidance, and regressions.

Keep product promo audio music-only with sparse percussion.

* fix(motion): stabilize artifact cards and capture declared covers

* fix(web): share creation type order with community

* test(motion): exercise audio controls through project UI lifecycle

* fix(motion): scope live cover behavior to actual video sources

* test(motion): use real HEAD responses in cover regressions

---------

Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com>
Co-authored-by: William <zhouhaoran@refly.ai>
Co-authored-by: open-design-crew[bot] <4197780+open-design-crew[bot]@users.noreply.github.com>
Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>
Co-authored-by: Amy <1184569493@qq.com>
2026-10-14 15:55:52 +00:00
bb9682a5f9 feat(daemon): make OD Next the default route, and keep the switch usable (#7876)
* feat(daemon): make OD Next the default route, and keep an opt-out opted out

An installation that configured nothing ran the ordinary strategy route, so
shipping OD Next in a build never changed how a run behaved until someone
opened Settings and asked for it. That made the Labs switch the whole rollout:
the strategy could only reach the users who already knew to look for it.

`resolveRequestedMode` now resolves an unconfigured installation to `active`.
Every other admission condition already defaulted open — assignment bucket,
both content flags, the production approval flag — so this last default was the
only thing holding the route closed.

The one-line change is the small part. Flipping the default inverts which case
is load-bearing. While the strategy was opt-in, an installation that lost its
saved mode kept the behaviour it already had; now an installation that loses
its saved mode is switched back on. So the invariant this has to keep is that
an installation which opted out reads `off` through every later release, and
that rests on opting out being stored rather than erased: `off` is a value the
config carries, `applyConfigValue` drops the key only for a value it cannot
store, and `assertWritableControlValues` refuses those on the way in.

That last guard is the same code as before but now protects the opposite
direction. It used to stop a typo from masquerading as an opt-out; it now stops
a typo from revoking one. Its docblock, the field comments on both sides of the
contract, the `od strategy` help text, and the prompt-composition note all
described the opt-in world and would have read as the reverse of the truth.

Tests carry the semantics:

- a new assertion pins the opt-out invariant, and checks not just the value
  read back but that the decision built from it actually evaluates to `off`;
- the route test now separates `default` from a saved `off`, which resolve to
  opposite routes and no longer to the same one;
- the app-config acceptance case against a real server is rewritten end to end.
  It was "nothing configured takes the ordinary route, and the same running
  daemon takes OD Next once you opt in". It is now the opt-out direction, and
  proves it from the agent invocation log rather than from the run record: the
  first run's prompt is the strategy's, the run after `off` is saved is not.

Packaged installs have no second control — the packaged child environment
allowlist carries no `OD_NEXT_*` key — so the saved mode is the only thing
standing between an opted-out user and this default.

e2e is deliberately untouched here and follows separately. Its specs mostly
never set this mode, so prototype and deck cases now take the OD Next route.
Pinning the env var in the harness is not the fix: the env var outranks the
saved config by design, which would break the spec asserting that an
app-config write takes effect with `app_config` as its source.

* test(daemon): stop the panel-grammar spec failing on the clock

`thinking-stream-panel-grammar` asserted that the whole SSE body contains
neither `Critic` nor `8.1`, on the stated premise that both "只可能来自标记内部".
That premise does not hold for the second one. The body also carries the
daemon's own `start` envelope, and `toolTokenExpiresAt` is an ISO timestamp: a
run whose token expiry lands on a second ending in `8` with milliseconds
starting with `1` produces `…:48.161Z`, which contains `8.1`. Roughly one run in
a hundred, and it hit on CI — with a thinking stream the log shows was clean,
so the spec failed on the clock rather than on a leak.

The two attribute-fragment assertions now run against the concatenated
`thinking_delta` / `text_delta` text instead of the raw body. That is not a
weaker check: persistence consumes exactly those deltas, so it is what the
spec's own "SSE clean ≡ database clean" argument was always about, and the
envelope fields it drops were never a place a leak could surface. The tag regex
stays on the whole body, where an opening `<PANELIST` cannot appear by accident.

The two prose assertions move to the same extracted text, which is what keeps
the negative assertions honest: an extractor that selected the wrong event type
would return an empty string, and every `not.toContain` would pass against it.

* feat(daemon): remove the OD Next stop latch

A run that hit certain failures wrote a row to `strategy_rollout_controls`.
From the next request on, every task on that installation took the legacy
route. The row outranked the saved mode, survived restart, and the Labs switch
kept reading "on" while the notice underneath said the harness had been paused —
with no control to unpause it. The only way back was an operator running
`od strategy rollout reset`.

The code already argued against this. The docblock over
`rolloutStopSignalForBlockedContinuation` says only a failure of the machine
contract itself may raise a latch, because an agent turn that mis-populates a
machine block "is already fail-closed for that task ... and it must not silently
return every later request in the daemon to the legacy path." One signal had
already been retired for doing exactly that: `complex_child_unverified` took OD
Next away from Codex, Claude and OpenCode because a fourth runtime lacks a
child-lifecycle producer. This generalises those narrowings instead of waiting
for the next signal to earn its own.

It also stopped being survivable. While OD Next was opt-in, the latch could only
reach the few installations that had asked for it; with the default flipped it
reaches everyone, and the population exposed to an automatic, restart-surviving,
UI-unrecoverable disable is now the whole install base.

Deleting it changes nothing about how any single failure is handled. All three
live trigger points were additive actions taken *after* the existing handling:
`native_resume_failed` already blocked the continuation, sent
`AGENT_SESSION_RESUME_FAILED` and finished the run failed; the blocked-
continuation cases were already set to `outcome: 'blocked'` with their reason
codes persisted for attribution. What goes away is only the side effect on every
later request. A fourth trigger — the run-duration threshold — never fired at
all: `OD_NEXT_STRATEGY_MAX_RUN_DURATION_MS` has no default anywhere, so the
comparison ran against NaN.

What is removed: the table and its migration, the write path and its telemetry
module, the single read in the run route, the reset endpoint, the
`od strategy rollout reset` subcommand, the latched lock in the Labs switch and
its 19 locale strings, and five contract types. `readOdNextRolloutControlStatus`
stays but no longer takes a database — the effective mode is now a pure function
of the requested mode and the content flags, so two callers reading it at the
same moment cannot disagree. Status is still worth an endpoint because
`requestedModeSource` names the authority, and `default` and a saved `off`
resolve to opposite routes.

Installations currently holding a latch recover on their own: with nothing left
to read the row, their next run is admitted normally. No migration needed, and a
downgrade is safe — older code recreates the table, reads it empty, and treats
the installation as unlatched.

Tests carry the invariant rather than a list. The cases that pinned which
signals were allowed to latch are replaced by one that pins the shape: the
decision is a function of the policy and this run's facts and takes no argument
through which an earlier run could reach it, so there is no signal left to
enumerate and none can be added by accident. The real-server case now asserts
that status still reports the deciding authority while both the reset endpoint
and its CLI subcommand are gone.

* fix(daemon): never leave the harness switch stuck, whatever the config says

Field report from a packaged build of this branch: the Design Harness switch
read on, greyed out, with "detected a problem, generation is using the original
approach" underneath, and every task on the machine taking the legacy route.
Nothing in the UI could clear it.

The diagnostics name the cause exactly. One agent turn ended mid-block —
`Unclosed <open-design-plan-contract> block`, 2 characters of visible text — and
that raised `machine_contract_leak`, whose stop mode is `off`, not `observe`. A
truncated response took the whole install off OD Next; the next daemon start
logged `requestedMode: 'active'`, `effectiveMode: 'off'`,
`od_next_rollout_stop_latched`, which is the screenshot.

That is not a rare condition. A stream ending early is the most ordinary failure
an agent has, and flipping the default hands it the entire install base.

Three changes, one property: the switch a user can see is a switch a user can
use.

1. The stop latch is removed (previously PR #7879, folded in here). A build that
   defaults on and still ships an automatic, restart-surviving, UI-unrecoverable
   disable is not shippable as two PRs — the window between them is exactly the
   build that was reported. Everything the latch did on top of the existing
   per-task handling is gone; the per-task handling is untouched.

2. An unreadable saved config no longer reads as an unconfigured one. The read
   path kept two states apart where it needed three: no file at all, a readable
   choice, and a config that exists but cannot be believed — malformed JSON, a
   non-object body, a mode this build does not recognise. The third used to
   collapse into the first harmlessly, because unconfigured meant `off` and so
   did an opt-out. Now unconfigured means `active`, so the same collapse turns
   "we cannot read your choice" into "you chose OD Next", against the
   installations most likely to have chosen the opposite. It resolves to `off`
   instead — the only answer that cannot override a real decision, and the
   recoverable one, since a user who never opted out sees the switch off and can
   turn it back on. `assertWritableControlValues` was never enough here: it
   guards writes through the API and can say nothing about a file that arrived
   bad, was hand-edited, or was written by another version.

3. A failed status read is retried from the switch instead of being permanent.
   It rendered the row locked and lifted only on remount, so a transient failure
   left a control that does nothing. A click on that lock now re-reads. The
   environment lock stays inert deliberately: the daemon would ignore the write,
   and pretending otherwise is worse than doing nothing — and no packaged
   install can reach it, since the packaged child env allowlist carries no
   `OD_NEXT_*`.

After this, the only greyed state a packaged user can reach is the moment before
the first read returns.

Installations already holding a latch recover on their own: with nothing left to
read the row, their next run is admitted normally. No migration, and a downgrade
is safe — older code recreates the table, reads it empty, and treats the
installation as unlatched.

Coverage follows the same split. The config cases run against real files through
`readAppConfig` rather than against the policy reader in isolation, because the
hole was in the join: truncated JSON, a non-object body, an unrecognised mode, a
typo, a non-string — each reads `off`, while a missing file and an explicit
`null` still reach the default. One case asserts the whole chain, saving `off`
then truncating the file and checking the mode survives, with a fresh
installation as the negative control so the guard cannot swallow the rollout it
protects. The UI case fails the first status read, clicks, and asserts the row
becomes operable.

* refactor(daemon): drop the JSDoc left above the removed latch classifier

The block described `MACHINE_CONTRACT_BOUNDARY_CODES` and
`rolloutStopSignalForBlockedContinuation`, both deleted with the stop latch.
Left in place it reads as documentation for the export below it, implying this
service still classifies machine-contract failures when it no longer does.

* fix(daemon): tell operators why `od strategy rollout reset` is gone

The command shipped in 0.21.0 and 0.21.1. Removing it left the subcommand
falling through to the generic usage dump, so an operator who scripted it saw a
list it was simply missing from and had to guess whether the command was
renamed, moved, or broken.

It now names itself: the daemon-instance stop latch it cleared no longer exists,
so there is nothing to reset, and the message points at the two commands that
replaced its use — `od config set odNextStrategyMode off` to opt out, and
`od strategy rollout status` to see which authority decides the mode.

It still exits non-zero rather than succeeding as a deprecated no-op. The
command meant "recover this machine's rollout". Reporting success for a recovery
that neither happened nor can happen is the same lie the latch itself told —
a control that says it acted while nothing changed — and this release is
removing that lie rather than relocating it into the CLI.

* fix(daemon): stop a broken config file from opting an installation out

The previous commit fixed a real hole — a saved `off` could be silently revoked
— but fixed it too widely and in the wrong direction for this rollout. It
treated a config file that failed to parse at all as evidence of an opt-out,
which it is not.

A file that will not parse resets every preference to its default: agent,
telemetry, all of it. Singling this one key out to resolve against its default
was inconsistent with the rest of the file, and the population it hits is the
wrong one — everyone whose config breaks, not everyone who opted out. Since the
second group is a subset of the first, the guard opted more installations out of
the rollout than it protected, in a change whose entire purpose is opting them
in.

The guard now covers only what it can actually see: `odNextStrategyMode` present
with a value that is not one of the modes — a hand edit, a typo, a mode written
by some other version. Something was configured and cannot be read, so it reads
`off` rather than becoming "you chose OD Next". A fresh install, a missing file,
an unparseable file and a config without the key all reach the default, which is
`active`.

That keeps both invariants this step owes: the default is on for anyone who has
not said otherwise, and a user who did say otherwise keeps their answer. The
switch stays operable in every one of these states, so nothing here can leave a
user looking at a control that does nothing.

* style(daemon): drop the blank line left at EOF by the classifier removal

The line separated `resolveAutomaticContinuationEvidence` from the JSDoc and
classifier below it. Removing those left the separator as a trailing blank line,
which `git diff --check` reports. No logic change.

* docs(daemon): stop enumerating which releases shipped `rollout reset`

The comment named 0.21.0 and 0.21.1. 0.22.0 shipped today and carries the
command too, so the enumeration was already wrong the day it was written --
any list of shipped versions goes stale by construction. Say "every release
since 0.21.0" instead, which stays true without maintenance.

The point the sentence is making is unchanged, and slightly stronger: the
surface has been out for three releases, so an operator who scripted it gets
told what happened rather than shown a help list it is missing from.

---------

Co-authored-by: Cheems <216108319+hihicheems@users.noreply.github.com>
2026-09-09 01:37:25 +00:00
6f081ef741 docs: map the two prompt implementations behind the OD Next fork (#7649)
* docs: map the two prompt implementations behind the OD Next fork

`composeSystemPrompt` returns early for OD Next runs, so the legacy stack below that return — the designer charter, the discovery layer, and the deck framework directive that embeds deck protocol v1 — never reaches those runs. Nothing in the repository said so, and prompt changes have been landing on one side only. The deck case is live: the OD Next `ppt.md` pins none of `data-od-deck-protocol`, the `od:deck-ready` announce, the `od:slide-state` posts, the `od:slide` listener, `id="deck-stage"`, or the print stylesheet, so a deck generated on that path can fail to be recognized as a v1 deck, paged from the host toolbar, or exported to PDF. Eligibility is re-evaluated per run and can latch down mid-session, which is why the divergence shows up as an intermittent bug.

Adds `apps/daemon/src/prompts/AGENTS.md` (fork point, the four variant axes, and a host runtime contract table naming which side carries each contract today) and `plugins/_official/scenarios/od-next-strategy/AGENTS.md` (asset roster and package-hash rules, and why maintenance notes must stay out of `assets/`, which is sent to the model verbatim). Root AGENTS.md gains an index entry and a Prompt variants section. Pointers go at the two fork sites, both deck-framework copies, and the OD Next atom registry, which is itself declared twice.

Documentation and comments only; no prompt bytes change. Validated with pnpm typecheck, pnpm guard, the daemon prompt suites (207 tests), and the contracts system-prompt / web-clone / deck-protocol suites (28 tests).

* docs: add PR #7568 as the worked example of a one-sided prompt change

The variant map says the two sides exist; it did not show what missing one costs a careful author. #7568 (merged 2026-08-29, `973e868ce`) is the case: it introduced deck protocol v1 across 18 files, handled the daemon/contracts prompt-copy split completely — shared constant, both copies changed, a guard added on each side — ran the full web and contracts suites plus two packaged DMG end-to-end runs, and touched nothing under `plugins/_official/scenarios/`. Its own stated goal was to stop new navigation dialects appearing; the OD Next path kept generating decks with no protocol at all, reported two days after merge.

Records the three reasons the miss stayed invisible, each still true today: both guards it added call `composeSystemPrompt` without an OD Next recipe so they cannot pass the fork (one is named 'ships new Agent decks with OD Deck Protocol v1' and is green), the prompt matrix snapshot updated cleanly because every scenario in it takes the legacy branch, and sharing a constant fixed only the machine-readable half — the surrounding directive prose had already drifted between the two copies before #7568 and still differs on main.

Documentation only. Validated with pnpm guard in a worktree on this branch.

* docs: mark the deck fork at the sites an agent actually edits

The file-top and fork-point pointers only reach an agent that reads from line 1. The edit an agent actually makes for a deck rule lands in the middle of DECK_FRAMEWORK_DIRECTIVE (368 lines below the daemon file's header) or at the push site (446 lines below the fork), typically reached by grepping and reading a short span — where no pointer was in view. Put a two-line marker at each of those four sites instead: above DECK_FRAMEWORK_DIRECTIVE in both prompt copies, and above the deck/freeform push guard in both, which covers both push branches. Line references in the two AGENTS.md files are re-pinned for the shift.

Comments only, placed outside every template literal. Verified byte-identical prompt output against origin/main: 8 scenarios x both composition paths, sha256 and length compared in one worktree with the source files swapped and contracts rebuilt — all 16 pairs identical. pnpm typecheck, pnpm guard, daemon tests/prompts (207), contracts system-prompt / web-clone / deck-protocol (28).

* docs: move the prompt variant map to docs/ and refresh it for #7651

Two changes in one, because main invalidated the old shape while it sat in review. Structural: the guidance moves from two nested AGENTS.md files to one `docs/prompt-composition.md`, matching how the root index already carries `docs/architecture.md` and `docs/skills-protocol.md`. Nested AGENTS.md is not auto-loaded on file access, so proximity bought no delivery that the root pointer and the in-file markers do not already provide. The plugin folder keeps a short AGENTS.md for the one rule that is only meaningful there — never write maintenance notes into `assets/`, which reaches the model verbatim. Naming it after the structure rather than the experiment keeps it valid if OD Next is retired or graduates: two of the four axes are independent of it.

Factual: #7651 closed the deck gap this document reported, by collapsing the scaffold to one source in contracts and reaching OD Next through `resolveOdNextDeckFrameworkMode` rather than the markdown assets. The contract table's deck rows now read supported on both sides, the daemon deck-framework file is a re-export, and the two markers that claimed OD Next never receives the directive are removed rather than reworded. #7568 and #7651 are now presented as a pair — a one-sided change and the follow-up nearly twice its size that repaid it. Remaining gaps are recorded: five daemon/contracts prompt pairs still hand-maintained, and `core-slim.ts` absent on the contracts side.

Documentation and comments only. Prompt output verified byte-identical against the merge base: 8 scenarios x both composition paths, sha256 and length, markers reverted and reapplied in place. pnpm typecheck (0 errors), pnpm guard, daemon tests/prompts (210), contracts system-prompt / web-clone / deck-protocol / od-next-prompt-recipe (53).

---------

Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com>
2026-09-01 06:26:59 +00:00