* docs(sync): add simplification roadmap
* docs(sync): strengthen simplification roadmap
* docs(sync): simplify sync implementation plan
Prioritize deletion, atomic single-tab ownership, and full-sync trigger consolidation while deferring compatibility removals and speculative abstractions.
* docs(sync): make simplification roadmap compatibility-safe
Narrow deletion work behind persisted-data, provider-eligibility, delivery, and request-cost gates. Keep schema readers and separate unrelated startup and maintenance correctness projects.
* docs(sync): fold code-verified review findings into plan
* docs(sync): align simplification plan with latest changes
* docs(sync): correct trigger-branch claim and add footprint estimate
* docs: cap services at 1000 LOC
* chore(lint): warn when a service exceeds 1000 lines
* chore(lint): raise service size cap to 1200 lines
* docs(sync): re-align simplification plan to master 6fefd741c5
* docs(sync): fold #9054 footprint auth into plan contracts
Master advanced 3 commits past the plan's 6fefd741c5 baseline. Exactly
one touches a task surface: #9054 (authenticated LWW project-move
footprint, GHSA-8pxh) landed in conflict-resolution.service.ts — the
file Task 6 carves up.
Contract 14 documented only #9045's decrypt-path check and emphasised
the weaker half ("synthetic ops are intentionally exempt; that exemption
must survive the disjoint-merge removal"). Read alone, that could bless
an op.entityIds read while removing disjoint-merge, reopening the vector
one merge removed. #9045's exemption sentence is still accurate at
master, so 14 was incomplete rather than wrong.
Name the invariant #9054 established: getTaskProjectMoveEntityIds
re-derives footprints from the authenticated payload.projectMoveFootprint
(LWW) / actionPayload.projectMoveSubTaskIds + actionPayload.task.id (raw
TASK_SHARED_UPDATE), never the plaintext envelope, and that one choke
point serves the disjoint-merge, local-win and superseded-op callers.
Also fence #9054 in §4 and re-align the baseline to 7ef7e69e96 (#9052
orphan-task navigation and #9056 locale touch no task surface).
* chore(lint): make the service size cap enforce on new services
'warn' could not enforce what AGENTS.md asserts. `ng lint` runs the
@angular-eslint builder, whose maxWarnings defaults to -1 and is not
overridden in angular.json, so warnings never fail a build: a new
1300-line service merged green. The documented flip-to-'error' gate
("once every *.service.ts is under the cap") needed
conflict-resolution.service.ts to go 3934 -> 1200, i.e. the whole
7-task simplification plan, so it would realistically never flip.
Ratchet instead: 'error' on **/*.service.ts, with the 8 pre-existing
offenders grandfathered to non-failing warnings at their real size, so
the debt stays visible and the list can only shrink.
Verified: a new over-cap service errors (exit 1); all 8 grandfathered
warn (exit 0); full `npm run lint:ts` is exit 0 with exactly 8 warnings
and 0 errors, which proves the list is complete — a missed offender
would fail the build. Every >1200-line @Injectable in the repo is named
*.service.ts, so the glob has no coverage hole, and .service.spec.ts is
exempt (checked against the 7797-line conflict-resolution spec).
Also correct AGENTS.md "1200 LOC": max-lines defaults to
skipComments/skipBlankLines false, so the cap counts physical lines.
* style(tasks): elevate add-subtask input
* fix(tasks): parse eml files without external dependency
Replace postal-mime with a bounded parser for sender, subject, and unencoded plain-text bodies. Ignore unsupported MIME body formats and document the root dependency policy.
* fix(tasks): isolate and harden eml import
Lazy-load the local parser, reject lossy or unsupported bodies, and store accepted text as literal notes so imported content cannot trigger remote resource loads. Document the title-only fallback and expand regression coverage.
* fix(tasks): decode eml headers and harden import parsing
- Decode RFC 2047 encoded-words in the subject and sender name so
international titles show "Grüße" instead of raw "=?UTF-8?...?=".
- Replace the charset regex with a quote-aware Content-Type parser so a
charset inside another quoted parameter can't be mistaken for the real
one, and ignore RFC 822 header comments (e.g. "7bit (comment)").
- Cap the synced title (300) and body (100k) so a crafted .eml can't
amplify into an oversized op (the literal fence can double the body).
- Document parseEml's intentional MIME omissions to prevent a later
"fix" that reopens the untrusted-body attack surface.
Make AGENTS.md the single source of guidance read natively by Claude
Code, OpenAI Codex, and GitHub Copilot. CLAUDE.md becomes a symlink to
it. Extract commit-message guidance into a reusable Agent Skill under
.agents/skills (read by Codex and Copilot); .claude/skills symlinks to
it for Claude Code, and .gitignore is narrowed to /.claude/* so only
that skills pointer is tracked. Drop the now-redundant
.github/copilot-instructions.md symlink since Copilot reads AGENTS.md.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- Remove dead test:shard:pfapi script from package.json
- Update AGENTS.md persistence layer path to op-log and sync
- Update documentation file paths in secure-storage.md,
vector-clocks.md, and quick-reference.md