Platform entry-point identity - thin spine vs explicit dual identity
TASK-IMP-138: Platform entry-point identity - thin spine vs explicit dual identity
Summary
On every consumer repo, install.sh writes root AGENTS.md as a thin workflow spine (task law, gates, HITL, memory pointer). On this platform repo, root AGENTS.md is the full Layer-1 memory protocol, CLAUDE.md duplicates it wholesale, and every pointer file (.cursorrules, GEMINI.md, .windsurfrules, copilot instructions, cursor rules) tells agents that root AGENTS.md is the first half of "canonical instructions". An agent on the platform repo that loads only the most-native file gets memory law and can miss task/HITL law entirely - the inverse of the installer's own documented rationale ("a root protocol file... would bury the workflow every agent needs"). This task resolves the identity - but WHICH resolution is a structural operator decision, presented here as a fork and deliberately not resolved by the author.
Implementation status: Branch A IMPLEMENTED (ship/batch-8f-entrypoint)
Operator chose Branch A (thin spine) on 2026-07-23 — see decision-branch-a.md and source_decisions. The fork block is closed; implementation remains deferred to Batch D on its own branch. Status stays ready_to_implement until that ship starts. Invariant clauses / ACs / test suite still hold under the chosen branch.
Branch A - thin spine everywhere. The platform repo's root AGENTS.md becomes the same thin workflow spine consumers get; the memory protocol's normative source moves to modules/memory/cyberos/data/AGENTS.md (already the vendored source of truth for installs), with .cyberos/memory/AGENTS.md as its installed copy; install.sh's is_platform_repo() exception (:515, :336) is deleted; CLAUDE.md becomes a pointer. Consequence: one identity everywhere, but a structural move of the protocol's normative home - links, tooling, and §0.4-adjacent wording that assume "root AGENTS.md is the protocol" must be swept (the walker cites AGENTS.md §3; memory docs reference sections by bare AGENTS.md).
Branch B - explicit dual identity. Root AGENTS.md stays the protocol on the platform repo, but the identity is declared everywhere it matters: root AGENTS.md gains a first-screen preamble ("on this platform repo this file is the Layer-1 memory protocol; task/HITL law lives at .cyberos/AGENT-ENTRY.md - read that first for work"), every pointer file's wording changes from "Canonical instructions: AGENTS.md (root) and ..." to name the workflow spine FIRST and label root AGENTS.md as the memory protocol, and CLAUDE.md either becomes a pointer or carries a marked-copy banner + a drift check against AGENTS.md. Consequence: no structural move, smaller diff, but the platform keeps a root file whose name means something different than it does on every consumer repo - permanently carried complexity.
Problem
Audit finding H6, verified first-hand 2026-07-23: the five pointer files all present root AGENTS.md as canonical instructions; on this repo that file is the memory protocol (AGENTS.md:1), and CLAUDE.md is a full second copy of it with no divergence guard. The installer itself implements the opposite default for consumers and documents why (install.sh:444-447), keeping the platform as a special case (:515) that nothing at the reading surfaces explains. Concrete failure modes: an agent reading only root AGENTS.md (the file the most tools read natively) learns memory law but not the two HITL gates or the never-push rule from the workflow side; and the AGENTS.md/CLAUDE.md byte-duplication forks silently the first time someone edits one (the protocol has already been amended repeatedly - P19..P22 - so edits are routine).
Proposed Solution
Present the fork (above), and once the operator picks a branch, implement it plus the branch-independent invariants: (1) an agent that reads only root AGENTS.md on this repo encounters, within the first screen of text, where task/HITL law lives; (2) every pointer file names the workflow spine unambiguously and describes root AGENTS.md's actual identity truthfully; (3) the memory protocol has exactly one normative source and every other copy declares itself a copy (pointer or marked duplicate with a drift check); (4) a grep-based test suite (scripts/tests/test_entrypoint_identity.sh) pins all three so the identity cannot silently regress. The suite is written to assert the invariants, not the branch, so it survives the decision.
Alternatives Considered
- Author resolves the fork (pick Branch A now). Rejected: the plan's approval boundary explicitly reserves this as an operator decision; Branch A moves the normative home of a protocol document that §0.2 protects with an amendment gate - exactly the kind of structural change an author must not smuggle through a task spec.
- Split into two tasks (one per branch), park both, close the loser. Rejected: two mutually-exclusive ready tasks in one backlog invite an agent to pick one by queue order - the opposite of an operator gate. One task, one recorded decision, one implementation.
- Do nothing; agents should read both files. Rejected: the audit's finding is precisely that "should" has no mechanism. The pointer files say "canonical", agents act on the first file loaded, and the failure mode (missing HITL law) is the highest-consequence class this hardening wave exists for.
- Fix only CLAUDE.md duplication and leave the identity question. Rejected: the duplication is a symptom; the identity mismatch between platform and consumer repos is the cause, and patching around it leaves H6 open.
Success Metrics
- Primary: within one release of the operator decision - an agent-eyes-first read of root
AGENTS.mdreaches task/HITL law (pointer or preamble) within the first 30 lines; all five pointer files describe the spine truthfully; exactly one normative protocol source exists with every copy self-declaring;test_entrypoint_identity.shgreen inrun_all.sh. Baseline today: 0 of the three invariants hold and no test exists. - Guardrail: no change to CONSUMER install behavior in Branch B (byte-identical spine); in Branch A, consumer installs are also unchanged (they already get the spine) - only the platform repo's own files move. The memory test suite stays green under either branch.
Scope
In scope: the operator decision record; root AGENTS.md, CLAUDE.md, and the five pointer files on this repo; install.sh's platform exception (Branch A deletes it; Branch B keeps + documents it); the invariant test suite; CHANGELOG.
Out of scope / Non-Goals
- Any change to the memory protocol's CONTENT (§0-§18 text) - this task moves/labels the container, never edits the law; protocol changes stay behind §0.2's approval grammar.
- Consumer-repo pointer file wording shipped by the installer (already truthful for consumers; if the operator wants consumer wording updates they ride the normal payload release, not this task).
- The BRAIN store layout and doctor wiring - TASK-MEMORY-303 (related: its INTEROP.md + this task's identity work both reduce the "which document governs me" ambiguity, from different ends).
Dependencies
None blocking on other tasks; blocked on the OPERATOR FORK above (this is a decision dependency, not a task dependency - deliberately not encoded in depends_on, which the queue reads as task-graph edges). Related: TASK-MEMORY-303 (memory-side contract hardening in the same wave).
AI Authorship Disclosure
- Tools used: Claude (Fable 5) running the CyberOS
task-authorskill in Cursor, as the task-authoring wave of the 2026-07-23 hardening plan. - Scope: the root/CLAUDE duplication, all five pointer files' wording, and the installer's spine + platform-exception code were verified first-hand at HEAD; the two branches are constructed from the installer's own documented rationale and the audit's H6 finding.
- Human review: the hardening plan was operator-approved 2026-07-23 with T8 explicitly reserved as an operator fork; this spec implements that reservation by specifying both branches and blocking on the pick.
1. Description (normative)
- 1.1 Implementation MUST NOT begin until an operator decision selecting Branch A or Branch B is recorded on this spec (a dated
source_decisionsentry) - the fork is the operator's, and an implementer arriving via the queue MUST halt at this clause if the record is absent. - 1.2 After implementation, root
AGENTS.mdon this repo MUST surface where task/HITL law lives within its first 30 lines - as the whole file (Branch A: it IS the spine) or as a preamble pointer (Branch B). - 1.3 Every pointer file (
.cursorrules,.cursor/rules/cyberos.mdc,GEMINI.md,.github/copilot-instructions.md,.windsurfrules,CLAUDE.md) MUST name.cyberos/AGENT-ENTRY.mdas the workflow entry and MUST describe rootAGENTS.md's identity truthfully for whichever branch was chosen. The "Canonical instructions: AGENTS.md (root) and ..." wording MUST NOT survive unqualified under Branch B. - 1.4 The memory protocol MUST have exactly one normative source file after this task (Branch A:
modules/memory/cyberos/data/AGENTS.md; Branch B: rootAGENTS.md), and every other copy MUST declare itself a copy - a pointer, or a marked duplicate protected by a drift check that fails CI when the copies diverge. - 1.5 Under Branch A only:
install.sh'sis_platform_repo()AGENTS.md exception MUST be removed, and the references that assume a root-file protocol home (walker citation strings, memory docs) MUST be swept to the new home. Under Branch B only: the exception MUST gain a comment pointing at this task's decision record. - 1.6 A new suite
scripts/tests/test_entrypoint_identity.shMUST assert the branch-independent invariants mechanically: first-30-lines task-law reachability in root AGENTS.md; spine naming in all six files of 1.3; single-normative-source (exactly one file without a copy/pointer marker); and (when a marked duplicate exists) the drift check runs and passes. It registers via therun_all.shglob. - 1.7
CHANGELOG.mdMUST record the chosen branch and the decision date.
2. Acceptance criteria
- [x] AC 1 (traces_to: #1.1) - the spec carries a dated operator decision entry naming the chosen branch BEFORE any implementation commit touches the files in 1.3-1.5 (verified by inspection of the spec's git history at review) - test:
scripts/tests/test_entrypoint_identity.sh::t01_decision_recorded - [x] AC 2 (traces_to: #1.2) -
head -30 AGENTS.mdcontains a reference to.cyberos/AGENT-ENTRY.md(or the spine content itself) - test:scripts/tests/test_entrypoint_identity.sh::t02_first_screen_reaches_task_law - [x] AC 3 (traces_to: #1.3) - each of the six files names
.cyberos/AGENT-ENTRY.md, and under Branch B none carries the unqualified "Canonical instructions: AGENTS.md (root)" phrasing - test:scripts/tests/test_entrypoint_identity.sh::t03_pointers_truthful - [x] AC 4 (traces_to: #1.4) - exactly one protocol file lacks a copy/pointer marker; every other file containing the protocol's H1 carries one; when a marked duplicate exists, the drift check passes and a mutated scratch copy makes it fail - test:
scripts/tests/test_entrypoint_identity.sh::t04_single_normative_source - [x] AC 5 (traces_to: #1.5) - Branch A:
is_platform_repono longer special-cases AGENTS.md and a repo-wide grep finds no stale root-protocol assumption in the swept references; Branch B: the exception carries the decision-record comment - test:scripts/tests/test_entrypoint_identity.sh::t05_branch_consistency - [x] AC 6 (traces_to: #1.6, #1.7) - the suite is discovered green by
bash scripts/tests/run_all.sh, and CHANGELOG's top entry names the chosen branch - test:scripts/tests/test_entrypoint_identity.sh::t06_registered_and_recorded
3. Edge cases
- The queue picks this task before the fork is decided: clause 1.1 is the halt - the implementer's first action is checking the decision record and halting to ask when absent. This is the designed outcome, not a failure; the task exists to force exactly one recorded decision.
- Branch A and §0.4's resolution language: the protocol text itself describes store resolution, not its own file location, so moving the normative home does not amend the law - but 1.5's sweep must verify no §-citation ("AGENTS.md §3" in walker/invariants strings) becomes ambiguous; where it would, the citation gains the explicit path.
- Tools that hard-read CLAUDE.md expecting full protocol text (Branch A): any such reader gets a pointer instead; the memory module's own data copy remains complete, so programmatic consumers (installer, walker) are unaffected - only prompt-layer readers change, which is the point.
- Divergence between AGENTS.md and CLAUDE.md discovered DURING implementation: the implementer must surface the diff to the operator before unifying - one of the copies contains edits the other never got, and choosing silently which survives is a protocol-content decision (§0.2 territory), not a container decision.
- Cursor's always-applied rules load both files today: after either branch the loaded set shrinks or gains pointers; the test suite's first-30-lines check keeps the workflow law reachable regardless of which file a tool loads first.
- Security-class: documentation/identity restructuring only; no execution surface, no data movement beyond text files; the §11 injection posture is unchanged (pointer files remain trusted repo content, not untrusted input).
Audit
§1 — Verdict summary
Seven §1 clauses, six ACs, six edge cases. The unusual shape - a spec that PASSES audit while implementation is BLOCKED on an operator fork - is deliberate and per the plan's approval boundary: the fork is presented to implementable depth on both branches, the acceptance surface is branch-independent, and clause 1.1 makes the halt mechanical for any implementer the queue routes here. The audit focused on whether the fork is genuinely undecided in the text (it must not smuggle a preference), whether the ACs survive either branch, and whether the block marker meets the §9.1-rule-5 discipline.
§2 — Findings (all resolved)
ISS-001 — the block was a prose note in the first draft, not a mechanical halt
"Operator decides" without a normative clause leaves the queue free to hand the task to an implementer who starts Branch A by taste. Resolved: clause 1.1 makes the recorded decision a MUST-precondition and names the halt behavior; AC 1 verifies the decision record precedes any implementation commit; the edge case declares the halt the designed outcome.
ISS-002 — first-draft ACs were Branch-A-shaped
Early ACs asserted "root AGENTS.md is the spine" - deciding the fork through the acceptance surface, which is the same smuggling clause 1.1 forbids. Resolved: ACs 2-6 assert branch-independent invariants (first-screen reachability, truthful pointers, single normative source, branch-consistency) with per-branch arms only where the branches genuinely diverge (AC 5).
ISS-003 — the CLAUDE.md divergence hazard was treated as future-proofing, not a live risk
AGENTS.md and CLAUDE.md are full duplicates TODAY, and the protocol has been amended four times (P19-P22) - a silent fork is one edit away, and unifying the copies during implementation could silently drop an edit one copy never received. Resolved: the edge case makes surfacing any discovered diff to the operator mandatory before unification (protocol-content territory, §0.2), and clause 1.4's drift check covers the marked-duplicate future.
ISS-004 — Branch A's blast radius was understated
Moving the protocol's normative home touches more than three files: walker citation strings ("AGENTS.md §3"), memory docs, and the installer exception all assume the root home. Resolved: the Branch A description names the sweep explicitly; clause 1.5's Branch A arm requires it; AC 5 greps for stale assumptions.
ISS-005 — "decision dependency" vs depends_on needed an explicit convention call
Encoding the fork as depends_on would wedge the task on a nonexistent task id; leaving it out entirely loses the block from the graph view. Resolved: Dependencies states the distinction (decision dependency, deliberately not a task-graph edge) and the body block marker carries it per §9.1 rule 5 - the queue-visible surface.
ISS-006 — verify axis and testability tension (structural docs task with grep ACs)
A verify: I task whose ACs cite a shell suite risks TRACE-friction (inspection claims with test citations). Resolved: the suite is scoped to the mechanically-checkable invariants (line-window greps, marker presence, drift check), while AC 1's decision-record half is explicitly review-time inspection - each AC names its real verification mode and the verify: I axis reflects the task's center of gravity.
§3 — TRACE-006 semantic sufficiency (per clause)
| Clause | Verb demand | Cited test asserts | Verdict |
|---|---|---|---|
| 1.1 MUST NOT begin until decision recorded | decision entry precedes implementation commits | AC 1: spec-history inspection + t01 asserts the entry exists | sufficient (inspection-mode, declared) |
| 1.2 MUST surface task law in first 30 lines | head-30 grep for the spine reference | AC 2: asserts the window | sufficient |
| 1.3 MUST name spine + truthful identity in six files | per-file greps incl. Branch-B negative phrasing | AC 3: asserts both halves | sufficient |
| 1.4 MUST have one normative source; copies declare | exactly-one-unmarked census + drift-check pass/fail | AC 4: asserts census AND mutated-copy failure | sufficient |
| 1.5 MUST remove exception + sweep (A) / comment it (B) | branch-scoped greps | AC 5: asserts the chosen branch's arm | sufficient |
| 1.6 MUST assert invariants, glob-registered | suite green under run_all discovery | AC 6: asserts registration | sufficient |
| 1.7 MUST record branch in CHANGELOG | substring in top entry | AC 6: asserts it | sufficient |
§4 — Resolution
Six findings - two fork-integrity, four material - all resolved in the audited revision. Score = 10/10.
Status transition draft -> ready_to_implement is authorised by this verdict per STATUS-REFERENCE.md §1.1 - with the explicit note that clause 1.1 blocks implementation on the operator fork; the task sits in the queue as the fork's forcing function, not as buildable-now work. The two human-acceptance gates in /ship-tasks are unchanged.
End of TASK-IMP-138 audit.