@geoly-ai/social-hub-cli 0.3.19 → 0.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/cmd-manifest.json +4 -2
- package/dist/content-review.test.js +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -1
- package/dist/index.js.map +1 -1
- package/dist/register-calendar-directives.d.ts +64 -0
- package/dist/register-calendar-directives.d.ts.map +1 -0
- package/dist/register-calendar-directives.js +135 -0
- package/dist/register-calendar-directives.js.map +1 -0
- package/dist/register-calendar-directives.test.d.ts +2 -0
- package/dist/register-calendar-directives.test.d.ts.map +1 -0
- package/dist/register-calendar-directives.test.js +127 -0
- package/dist/register-calendar-directives.test.js.map +1 -0
- package/package.json +2 -2
- package/skills/README.md +23 -22
- package/skills/manifest.json +6 -1
- package/skills/reddit-content-rewrite/SKILL.md +29 -0
- package/skills/reddit-content-writing/SKILL.md +53 -4
- package/skills/reddit-delivery-check/SKILL.md +38 -6
- package/skills/reddit-matrix.lock.json +6 -6
- package/skills/reddit-phase-brief/SKILL.md +39 -4
- package/skills/reddit-phase-brief/references/phase4-brief-workflow.md +43 -3
- package/skills/reddit-strategy-shared/references/content-diversity-contract.md +7 -2
- package/skills/reddit-strategy-shared/references/content-review-gate.md +48 -1
- package/skills/reddit-strategy-shared/references/handoff-schemas.md +72 -2
- package/skills/reddit-strategy-shared/references/native-copy-profile.md +207 -0
- package/skills/reddit-strategy-shared/references/persona-card-contract.md +210 -0
- package/skills/reddit-strategy-shared/references/version-manifest.md +60 -0
- package/skills/reddit-subreddit-compliance/SKILL.md +28 -1
- package/skills/social-hub-calendar-directives/SKILL.md +548 -0
- package/skills/social-hub-calendar-jobs/SKILL.md +10 -0
- package/skills/social-hub-cli/SKILL.md +2 -0
- package/skills/social-hub-notifications/SKILL.md +7 -0
- package/skills/social-hub-publishing/SKILL.md +5 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reddit-delivery-check
|
|
3
3
|
description: |
|
|
4
|
-
Reddit Phase 5 delivery QA skill. Use to verify final strategy deliverables, execution table coverage, batch draft completeness, SKU and brand-word quotas, external-comment audit status, brand-risk review status, unresolved placeholders, and the v2.12.25 structure gates before client delivery: client content-prototype mapping, StructureFamily preservation, rewrite structure drift, AnchorSlot legality, rolling-window structure collisions, subreddit baseline freshness, narrative-fingerprint / final-draft hash match, Hub optimized-draft structure override,
|
|
4
|
+
Reddit Phase 5 delivery QA skill. Use to verify final strategy deliverables, execution table coverage, batch draft completeness, SKU and brand-word quotas, external-comment audit status, brand-risk review status, unresolved placeholders, and the v2.12.25 structure gates before client delivery: client content-prototype mapping, StructureFamily preservation, rewrite structure drift, AnchorSlot legality, rolling-window structure collisions, subreddit baseline freshness, narrative-fingerprint / final-draft hash match, Hub optimized-draft structure override, fabricated human signals (deliberate typos, fake community history, invented identity), Persona Card preservation, correct reporting of not-assessed review dimensions, compliance-evidence freshness at delivery time, and acceptance-profile eligibility.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Reddit Delivery Check
|
|
@@ -18,6 +18,9 @@ Mandatory before running the structure gates:
|
|
|
18
18
|
|
|
19
19
|
- `~/.cursor/skills/reddit-strategy-shared/references/content-type-routing-contract.md`
|
|
20
20
|
- `~/.cursor/skills/reddit-strategy-shared/references/content-diversity-contract.md`
|
|
21
|
+
- `~/.cursor/skills/reddit-strategy-shared/references/persona-card-contract.md`
|
|
22
|
+
- `~/.cursor/skills/reddit-strategy-shared/references/content-review-gate.md` for how a `not_assessed` dimension must be reported and why scores are comparable only within one `applicabilitySignature`
|
|
23
|
+
- `~/.cursor/skills/reddit-strategy-shared/references/native-copy-profile.md` when the batch declared `AcceptanceProfile: NativeCopy`
|
|
21
24
|
|
|
22
25
|
## Required Inputs
|
|
23
26
|
|
|
@@ -35,7 +38,7 @@ Mandatory before running the structure gates:
|
|
|
35
38
|
- verifier output when available
|
|
36
39
|
- external post audit results
|
|
37
40
|
|
|
38
|
-
## Structure Checks (
|
|
41
|
+
## Structure Checks (all ten must pass)
|
|
39
42
|
|
|
40
43
|
Delivery is blocked unless every one of these verifies. This layer creates no
|
|
41
44
|
diversity by itself; it exists so the earlier three layers cannot be skipped.
|
|
@@ -90,11 +93,25 @@ diversity by itself; it exists so the earlier three layers cannot be skipped.
|
|
|
90
93
|
`this sub always...`), no invented identity, and no flaw inserted only to
|
|
91
94
|
look authentic.
|
|
92
95
|
|
|
93
|
-
**
|
|
96
|
+
10. **Persona Card preserved.** Every delivered main-post Key's
|
|
97
|
+
`PersonaCardRef` / `PersonaCardVersion` / `PersonaCardHash` equal the Brief's
|
|
98
|
+
assigned Card, and `PersonaCardSelfCheck: Pass` was recorded **against that
|
|
99
|
+
same version**. A self-check recorded against an earlier version is stale, not
|
|
100
|
+
a pass — a Card change invalidates every boundary check made under the old
|
|
101
|
+
one. A Card authored or edited downstream of `reddit-phase-brief` is a
|
|
102
|
+
failure with `FixOwnerSkill: reddit-phase-brief`, whichever skill wrote it.
|
|
103
|
+
A missing Card on a post row is a `BlockingGap`, never "no Card, so nothing
|
|
104
|
+
to check". Carry `PersonaCardAuthorizationStatus` through from the Brief:
|
|
105
|
+
`DeclaredOnly` does not block delivery, but the delivery record may not
|
|
106
|
+
describe that row's firsthand claims as authorised. Do not read `RoleLabels`
|
|
107
|
+
in this check, and do not report the Card as a verified identity or as
|
|
108
|
+
anything Hub assessed.
|
|
109
|
+
|
|
110
|
+
**Scope.** Checks 1–7 and 10 apply to **main-post Keys only** (`WindowScope: PostOnly`).
|
|
94
111
|
Comments, self-comments, and external comments need no fingerprint row, no
|
|
95
|
-
prototype mapping, no `StructureFamily`, no `SampleSetID`,
|
|
96
|
-
they still need Hub `completed/pass` plus `FinalDraftSha256`, and
|
|
97
|
-
still apply to them.
|
|
112
|
+
prototype mapping, no `StructureFamily`, no `SampleSetID`, no `AnchorSlot`, and no
|
|
113
|
+
Persona Card; they still need Hub `completed/pass` plus `FinalDraftSha256`, and
|
|
114
|
+
checks 8–9 still apply to them.
|
|
98
115
|
|
|
99
116
|
Missing index file, missing `Final` row for a post Key, zero or multiple active
|
|
100
117
|
rows for one Key, unlocked window parameters, or a missing/expired baseline are
|
|
@@ -114,6 +131,10 @@ introduced by rewriting, set `FixOwnerSkill: reddit-content-rewrite`.
|
|
|
114
131
|
- ContentOnly `completed/pass` is acceptable for a content handoff, but it is not a Phase 5 delivery pass and must show `AccountBindingStatus: Pending` and `ReadyForDelivery: No`.
|
|
115
132
|
- No AccountBound delivered item has unresolved `brandFitReviewRequired=true` or blocking `accountCandidateFeedback`; the approved `brandContext` and resolution are recorded.
|
|
116
133
|
- The review log covers every final draft, and any `revise` feedback is reflected in the exact delivered text. `degraded`, unavailable, or local-only review is a failure.
|
|
134
|
+
- Every dimension a Hub result marked `not_assessed` is carried into the delivery record as NotAssessed with its `notAssessedReason` — never as a low score, a passing score, or a partial check, and never averaged into any figure. The `score` left on such a dimension is diagnostic only. `unevaluatedDimensions` on a `degraded` result is a *different* thing (assessable but unanswered) and must not be reported as not-applicable. No delivery figure compares or averages scores across different `applicabilitySignature` values — a ContentOnly score and an AccountBound score are not the same measurement.
|
|
135
|
+
- Compliance evidence is still fresh **as of delivery**. Re-check the Compliance Output handoff's `EvidenceGatheredAt` / `EvidenceValidUntil` under the definition that handoff sets (`../reddit-strategy-shared/references/handoff-schemas.md`): they are batch-level **worst-case** scalars over the five delivery-required items (`RulesEvidenceStatus` / `PinnedEvidenceStatus` / `BotBannerEvidenceStatus` / `InstalledAppsStatus` / `AboutPageEvidenceStatus`) — earliest gathering time and earliest validity horizon. `EvidenceValidUntil` in the past, or `Unknown` on either scalar, holds the affected rows for re-gathering and re-judgement. A clearance recorded once at compliance time does not satisfy this.
|
|
136
|
+
- Separately from freshness: where the board's brand/vendor permission or a disclosure requirement was **never adjudicated**, it stays in `MissingEvidence` with the corresponding `PostRisk` / `CommentRisk` and needs a recorded human decision. This is not a freshness question and no timestamp resolves it — re-gathering evidence re-opens the question, it never answers it, and the answer is never inferred from a style profile's brand-friendliness note or from raw collected evidence.
|
|
137
|
+
- When the batch declared `AcceptanceProfile: NativeCopy`, `NativeCopyProfileStatus` is recorded and no row is `ReadyForDelivery: Yes` while it is `Unavailable(ServerContractMissing)`. A batch that was quietly re-labelled `Default` to release delivery, or whose review call was weakened until it succeeded and then counted as a profile pass, is a failure.
|
|
117
138
|
- No placeholders, unresolved evidence gaps, or unmerged side tables remain.
|
|
118
139
|
- Master Canvas has all 21 boards, including Data Support, MCP Status, and Verification Boundaries.
|
|
119
140
|
- Feishu draft delivery uses a single draft document unless the user explicitly requests otherwise.
|
|
@@ -138,6 +159,10 @@ introduced by rewriting, set `FixOwnerSkill: reddit-content-rewrite`.
|
|
|
138
159
|
- FingerprintHashIssues:
|
|
139
160
|
- HubStructureConflictIssues:
|
|
140
161
|
- FabricatedSignalIssues:
|
|
162
|
+
- PersonaCardIssues:
|
|
163
|
+
- NotAssessedReportingIssues:
|
|
164
|
+
- EvidenceFreshnessIssues:
|
|
165
|
+
- AcceptanceProfileIssues:
|
|
141
166
|
- PlaceholderIssues:
|
|
142
167
|
- RequiredFixes:
|
|
143
168
|
- DeliverySummary:
|
|
@@ -160,6 +185,13 @@ introduced by rewriting, set `FixOwnerSkill: reddit-content-rewrite`.
|
|
|
160
185
|
- FingerprintHashStatus: Matched / Mismatch / Pending
|
|
161
186
|
- HubStructureConflictStatus: None / Escalated
|
|
162
187
|
- FabricatedSignalStatus: None / Found
|
|
188
|
+
- PersonaCardStatus: Preserved / Drift / Missing / NotApplicable
|
|
189
|
+
- PersonaCardAuthorizationStatus: Authorized / DeclaredOnly / NotApplicable # carried through from the Brief; DeclaredOnly does not block but may not be reported as authorised
|
|
190
|
+
- EvidenceFreshnessStatus: Fresh / Expired / Unknown
|
|
191
|
+
- AcceptanceProfile: Default / NativeCopy
|
|
192
|
+
- NativeCopyProfileStatus: NotSelected / Unavailable(ServerContractMissing)
|
|
193
|
+
- LocalReviewIntent: accountBinding=<account_bound|persona_less>, brandTreatment=<managed_brand|organic> # carried through; local declaration only
|
|
194
|
+
- HubReviewScopeStatus: Unavailable(ServerContractMissing)
|
|
163
195
|
- ReviewMode: ContentOnly | AccountBound
|
|
164
196
|
- AccountBindingStatus: Pending | Resolved | NeedsReview
|
|
165
197
|
- ReadyForDelivery: Yes / No
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"contentSha256": "9a4fa3944ec3913a4e68cb1faf4d4a32ca04131fa4b0c37e054065f4e2c1a1ed"
|
|
8
8
|
},
|
|
9
9
|
"reddit-strategy-shared": {
|
|
10
|
-
"contentSha256": "
|
|
10
|
+
"contentSha256": "bba5c177a4af9bab0f2d9c227993862629e82ec9747cd2788e4874d058da3c89"
|
|
11
11
|
},
|
|
12
12
|
"reddit-phase-research": {
|
|
13
13
|
"contentSha256": "9f47b8eb0d1c1c194c685856242c9b22d190dfa8c6f572869e964239215795fe"
|
|
@@ -16,16 +16,16 @@
|
|
|
16
16
|
"contentSha256": "8080adce62b6db65be3dbc0f37f162a81e382d6b64108f7352ec83c356664659"
|
|
17
17
|
},
|
|
18
18
|
"reddit-phase-brief": {
|
|
19
|
-
"contentSha256": "
|
|
19
|
+
"contentSha256": "3475bf4c4aa4796db1c99a3bd527c52dc8488f71db01753da0325e924d552b99"
|
|
20
20
|
},
|
|
21
21
|
"reddit-content-writing": {
|
|
22
|
-
"contentSha256": "
|
|
22
|
+
"contentSha256": "08023a5dfa6cc2f3ffc3f1a551479c645ddcfc8cc775f88cd2d245c067829482"
|
|
23
23
|
},
|
|
24
24
|
"reddit-content-rewrite": {
|
|
25
|
-
"contentSha256": "
|
|
25
|
+
"contentSha256": "2f9dfdbb2410d060f755860b57ebc7c6776f4df343990029a8770855ecd33005"
|
|
26
26
|
},
|
|
27
27
|
"reddit-delivery-check": {
|
|
28
|
-
"contentSha256": "
|
|
28
|
+
"contentSha256": "9231ff9ae5d950bc2fb87e45bee05e793b3ff9c37bee45e8444059a8b698e4a9"
|
|
29
29
|
},
|
|
30
30
|
"reddit-voc-volume": {
|
|
31
31
|
"contentSha256": "496c0d57af7507d86a16bd4d06f3a5a017ca10b3dc4e3564d82b0c988e1c87ac"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"contentSha256": "dbba80345ebe68818a25e7604dfaa6951c6bf0e4d785d5013be7a06b22ec5b7c"
|
|
35
35
|
},
|
|
36
36
|
"reddit-subreddit-compliance": {
|
|
37
|
-
"contentSha256": "
|
|
37
|
+
"contentSha256": "3cab43d1c70d511212f3684929a8d7da73c11d3ae34563f31d1eebe04a6ecd8b"
|
|
38
38
|
},
|
|
39
39
|
"reddit-answers-research": {
|
|
40
40
|
"contentSha256": "eb132d81021f0a24619d9b576a287f722cf61c24422874d5be86cd4be2c392df"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reddit-phase-brief
|
|
3
3
|
description: |
|
|
4
|
-
Reddit Phase 4 Brief skill for the matrix SOP. Use after reddit-phase-planning completes Phase 4.0.6 and before reddit-content-writing starts. It locks the target flair first by verifying planning's `ProposedFlair` from the scheduling ledger, then establishes the flair-level subreddit baseline, then locks ContentFunction and StructureFamily, then the outline; it also creates board rationale, content schedule, per-Key title/direction/outlines, brand/SKU placement strategy, prewriting control fields (AssignedFlair / PostingTrigger), structure pre-assignment (OpeningMove / EvidenceShape / BrandEntryMode / EndingMode / LengthBand / SampleSetID / RecentCollisionCheck), and the approval gate. Trigger when the user says "确认进入 Phase 4 Brief", "开始 Brief", "产出 Brief 汇报稿", or when planning is complete but drafting has not started.
|
|
4
|
+
Reddit Phase 4 Brief skill for the matrix SOP. Use after reddit-phase-planning completes Phase 4.0.6 and before reddit-content-writing starts. It locks the target flair first by verifying planning's `ProposedFlair` from the scheduling ledger, then establishes the flair-level subreddit baseline, then locks ContentFunction and StructureFamily, then the outline; it also creates board rationale, content schedule, per-Key title/direction/outlines, brand/SKU placement strategy, prewriting control fields (AssignedFlair / PostingTrigger / PersonaCardRef), structure pre-assignment (OpeningMove / EvidenceShape / BrandEntryMode / EndingMode / LengthBand / SampleSetID / RecentCollisionCheck), and the approval gate. Trigger when the user says "确认进入 Phase 4 Brief", "开始 Brief", "产出 Brief 汇报稿", or when planning is complete but drafting has not started.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Reddit Phase Brief
|
|
@@ -17,6 +17,8 @@ Read the shared contracts first:
|
|
|
17
17
|
- `~/.cursor/skills/reddit-strategy-shared/references/context-contract.md`
|
|
18
18
|
- `~/.cursor/skills/reddit-strategy-shared/references/content-type-routing-contract.md` (mandatory)
|
|
19
19
|
- `~/.cursor/skills/reddit-strategy-shared/references/content-diversity-contract.md` (mandatory)
|
|
20
|
+
- `~/.cursor/skills/reddit-strategy-shared/references/persona-card-contract.md` (mandatory — this skill **owns** the Persona Card)
|
|
21
|
+
- `~/.cursor/skills/reddit-strategy-shared/references/native-copy-profile.md` only when the batch runs `AcceptanceProfile: NativeCopy`
|
|
20
22
|
|
|
21
23
|
Then verify these project memory files exist:
|
|
22
24
|
|
|
@@ -95,9 +97,26 @@ outline before the structure is decided from evidence.
|
|
|
95
97
|
7. **Lock the project diversity window** (`WindowCount` 8–12, `WindowDays` 30–60) if not already locked, and read the rolling window from `memory/phase4-叙事指纹索引.md`.
|
|
96
98
|
8. Produce board-selection rationale with evidence URLs, then the board overview table.
|
|
97
99
|
8b. **Run the material-modality preflight (G4, step B-1.4) before locking any structure.** Every selling point / material carries `MaterialID` + `OriginalModality` (`Required` / `Preferred` / `Optional` / `Forbidden`) + `EffectiveModality` + `ModalitySource`. Upgrading modality is blocked unless an `AuthoritativeSource` (`ClientContract` / `SubredditRule` / `ComplianceRequirement` / an already-authorized `ApprovedBrief`) backs it via `ModalityUpgradeRef`, with `ApprovedBy` ≠ the generating agent. **A material the user explicitly marked `Optional` is never upgraded** — go back to the user instead. Missing or conflicting modality is fail-closed. Disclosure/compliance requirements are not selling-point modality.
|
|
98
|
-
9. For every post Key: resolve `客户内容原型` → `MappingRuleID`, lock `ContentFunction`, confirm the `EvidenceShape` actually in hand, narrow `StructureFamily` candidates with the ordered filter in `content-type-routing-contract.md`, pre-assign `OpeningMove` / `CaveatMode` / `EndingMode` / `LengthBand` / `AnchorSlot` (**`BrandEntryMode` is deliberately excluded here — it is assigned in 9b, after its gate**), and **record (not yet validate)** the `PostingTrigger` candidate — its cross-constraint with `BrandEntryMode` can only be checked after 9b(受控值 `ConcreteFailure` / `PurchaseDecision` / `MeasurementResult` / `CompatibilityGap` / `UnsolicitedTestimony`;`UnsolicitedTestimony` 仅当 `BrandEntryMode: 不出现` 且有真实非征集非品牌控制的 testimony 证据),and **only then** — after 9b has supplied `BrandEntryMode` — run `RecentCollisionCheck` on the complete triple. On collision, first swap `OpeningMove` / `EndingMode` within the same family; reselect a family only if that fails. **Only then** write the outline.
|
|
100
|
+
9. For every post Key: resolve `客户内容原型` → `MappingRuleID`, lock `ContentFunction`, confirm the `EvidenceShape` actually in hand, narrow `StructureFamily` candidates with the ordered filter in `content-type-routing-contract.md`, pre-assign `OpeningMove` / `CaveatMode` / `EndingMode` / `LengthBand` / `AnchorSlot` (**`BrandEntryMode` is deliberately excluded here — it is assigned in 9b, after its gate**), and **record (not yet validate)** the `PostingTrigger` candidate — its cross-constraint with `BrandEntryMode` can only be checked after 9b(受控值 `ConcreteFailure` / `PurchaseDecision` / `MeasurementResult` / `CompatibilityGap` / `UnsolicitedTestimony`;`UnsolicitedTestimony` 仅当 `BrandEntryMode: 不出现` 且有真实非征集非品牌控制的 testimony 证据),and **only then** — after 9b has supplied `BrandEntryMode` — run `RecentCollisionCheck` on the complete triple. On collision, first swap `OpeningMove` / `EndingMode` within the same family; reselect a family only if that fails. **Only then** write the outline. Under `AcceptanceProfile: NativeCopy` a client requirement about how posts end narrows **the set this step may assign from** — it is never a question appended to a finished draft, since `EndingMode` is a locked field and one of the three collision fields. If the narrowed set leaves no collision-free option, that is a `BlockingGap` back to planning (`../reddit-strategy-shared/references/native-copy-profile.md`).
|
|
99
101
|
9b. **Only now assign `BrandEntryMode`, and only through `BrandPlacementGate` (G2):** for **P-B / PGC posts**, placement is decided by the brand-first-mention histogram plus the §2 risk ceiling (`SelectedPlacement` = the later of the two, histogram bucket 结论 mapping to 后段; the domain is 标题/首段/中段/后段, and a class-D board or `NoBrandMentionRate ≥ 0.8` that leaves no viable spot is a `BlockingGap` back to planning, never a silent drop to 不出现/评论) — **never derived from the P-B label**. **P-NB posts** go straight to `不出现` without running the formula; **C-B comments** use `CommentPlacementDecision` plus the §2 ceiling and complement rules. Fill the placement evidence first, **branching by row type**: P-B / PGC posts need a complete `BrandPlacementBasis`; P-NB posts take `N/A(P-NB · 主帖不露品牌)`; C-B comments take `InheritedBrandPlacementBasis` + `CommentPlacementDecision` instead and are never required to carry their own. Legitimate title exposure is preserved for `PGCApproved` (precedent check passed, board allows vendor/official, flair/disclosure/frequency met) and `BrandReviewPost` (genuinely a post about that brand, judged from `PrimaryPostType` / `WritingVariant` plus topic evidence — never by inventing a `ContentFunction: Review` value). This gate governs *what decides placement only* — brand quota semantics (P-B / P-NB / C-B / C-NB / PGC) are untouched.
|
|
100
102
|
9c. **Narrative layering (G1, step B-1.6) before writing title directions or outlines:** every post and comment carries `InternalCoverageIntent` (FAQ ids, fanout queries, keywords, gap codes, `prompt_id` — internal only) and a non-empty `ReaderVisiblePremise`. The gate scans only reader-facing fields (核心角度 / 标题方向 / 大纲 / 撰写逻辑一句话 / 评论任务方向 / outbound Reddit payload); Routing Decision, TraceFields and 方向选取理由 keep their internal labels. Converting FAQ evidence into a real reader question is the intended outcome — only raw internal labels in the narrative are blocked.
|
|
103
|
+
9d. **Assign the Persona Card for every main-post Key (contract:
|
|
104
|
+
`../reddit-strategy-shared/references/persona-card-contract.md`).** The Card is the
|
|
105
|
+
third `PrewritingControlField` and this skill owns it: record `PersonaCardRef` +
|
|
106
|
+
`PersonaCardVersion` + `PersonaCardHash`, the capability fields
|
|
107
|
+
(`knowledgeAccess` — exactly one of `research` / `owned` / `used` / `observed` /
|
|
108
|
+
`supplied_by_brand`; `permittedExperienceClaims` — a possibly-empty subset of
|
|
109
|
+
`{ownership, delivery, wear, service}`; `attentionFocus`; `questionIntent`), and the
|
|
110
|
+
per-draft fields (`postingWhy`, `knows[]`, `doesNotKnow[]`, `permittedFactRefs`,
|
|
111
|
+
`brandRelationshipDisclosed`). `postingWhy` must be able to coexist with the assigned
|
|
112
|
+
`PostingTrigger` — they are different fields and neither substitutes for the other.
|
|
113
|
+
**The writer never authors a Card**; letting it do so would let the draft grant itself
|
|
114
|
+
whatever boundary it already needs. Comments / self-comments / external comments take
|
|
115
|
+
`N/A` — this train does not put comments under a Card. `permittedExperienceClaims` is
|
|
116
|
+
authorised from submitted context, never inferred from the brand relationship: a
|
|
117
|
+
partner may genuinely own the product, and an empty set is a normal Card. The Card is a
|
|
118
|
+
**declared posting context, not a verified identity** — it is `LocalOnly`, is never
|
|
119
|
+
sent to Hub, and never implies the publishing account was resolved.
|
|
101
120
|
10. Create one Brief block for every execution-table Key in the selected batch, including posts, self-comments, and external comments.
|
|
102
121
|
11. For all P-B/C-B/PGC rows, record brand/SKU placement rationale using `references/brand-SKU-PGC-rules.md` (§3's five sample-learning classes are *rationale records only* — they never write `SelectedPlacement` directly nor bypass §2.0's mechanical rules).
|
|
103
122
|
12. Write a `Planned` fingerprint row for every briefed post Key into `memory/phase4-叙事指纹索引.md` (`DraftHash: PENDING`), including the two `PrewritingControlFields` (`AssignedFlair`, `PostingTrigger`).
|
|
@@ -115,6 +134,14 @@ Baseline cards may live in `memory/phase4-撰写brief.md` or a dedicated baselin
|
|
|
115
134
|
file; either way each is addressed by its `SampleSetID` and is reusable across
|
|
116
135
|
Keys and batches within its validity period.
|
|
117
136
|
|
|
137
|
+
Persona Cards may likewise live inside `memory/phase4-撰写brief.md` or in a
|
|
138
|
+
dedicated `memory/phase4-persona-card.md`; either way each is addressed by its
|
|
139
|
+
`PersonaCardRef` and carries its own `PersonaCardVersion` / `PersonaCardHash`.
|
|
140
|
+
Unlike a baseline card a Persona Card is **per post Key, not shared** — its
|
|
141
|
+
per-draft fields belong to one submission. Do not record Cards in
|
|
142
|
+
`memory/phase4-叙事指纹索引.md`: the index has a fixed schema and a fixed hash
|
|
143
|
+
definition, and the Card is not a fingerprint field.
|
|
144
|
+
|
|
118
145
|
Optional Feishu Brief documents require an explicit user-provided folder link.
|
|
119
146
|
|
|
120
147
|
## Gate
|
|
@@ -124,7 +151,8 @@ Writing may start only after:
|
|
|
124
151
|
- Every target subreddit has a valid, unexpired baseline card with a `SampleSetID`, with flair coverage recorded (`BaselineScope` / `FlairCoverageStatus`); `FlairCoverageStatus` is `Exact` or `ApprovedFallback` (never `Insufficient`).
|
|
125
152
|
- Every **main-post** Key carries a non-empty `AssignedFlair` — verified against the ledger's `ProposedFlair` and the subreddit's live flair list (`FlairVerificationStatus: Verified` or `FallbackAssigned`; no `Rejected` left open), or the sentinel `NoFlairAvailable` / `NoFlairRequired` for flairless boards — and its `AssignedFlair` matches the baseline card's covered flair (or an approved fallback).
|
|
126
153
|
- Every **main-post** Key's `客户内容原型` resolves to an `Approved` mapping row (`MappingRuleID` / `MappingVersion` recorded in the Routing Decision). Self-comments inherit the paired post's value; external comments use `—` and need no mapping of their own.
|
|
127
|
-
- Every post Brief row carries the nine `LockedFingerprintFields` (`ContentFunction`, `StructureFamily`, `OpeningMove`, `EvidenceShape`, `BrandEntryMode`, `CaveatMode`, `EndingMode`, `LengthBand`, `AnchorSlot`), the
|
|
154
|
+
- Every post Brief row carries the nine `LockedFingerprintFields` (`ContentFunction`, `StructureFamily`, `OpeningMove`, `EvidenceShape`, `BrandEntryMode`, `CaveatMode`, `EndingMode`, `LengthBand`, `AnchorSlot`), the three `PrewritingControlFields` (`AssignedFlair`, `PostingTrigger` — satisfying the `PostingTrigger × BrandEntryMode` cross-constraint — and `PersonaCardRef`), and the four `TraceFields` (`SampleSetID`, `RecentCollisionCheck`, `MappingRuleID`, `MappingVersion`).
|
|
155
|
+
- Every **main-post** Key carries a non-empty `PersonaCardRef` with its `PersonaCardVersion` / `PersonaCardHash`, a single `knowledgeAccess`, a `permittedExperienceClaims` set drawn only from `{ownership, delivery, wear, service}` (empty is valid), non-empty `postingWhy`, and a `postingWhy` that can coexist with the assigned `PostingTrigger`. Comments take `N/A`. Role labels, if present, are non-authoritative and no gate reads them. Any Card with a non-empty `permittedExperienceClaims` also carries `DeclaredBy` (never the generating agent) and `ClaimAuthorizationRef`; without both, the claim set is reported as a Brief declaration rather than an authorised set.
|
|
128
156
|
- `RecentCollisionCheck` is `Pass` for every row, or `Exception` with an approved `ExceptionRef`.
|
|
129
157
|
- Every **P-B / PGC post** row carries a complete `BrandPlacementBasis` and a `SelectedPlacement` consistent with it (an already-planned P-B/PGC row may only land on 标题/首段/中段/后段 — moving the brand to a comment or dropping it is a quota change, so it goes back to planning); **C-B comments** carry `InheritedBrandPlacementBasis` + `CommentPlacementDecision` instead and never build their own histogram. No row derives title-brand exposure from the P-B label; `NoComparableEvidence` rows went through re-sampling / StopAndAsk / a recorded `BrandTitleExposureException`, not a default; `Sparse` rows meet the three §2.5 conditions.
|
|
130
158
|
- Every post and comment Brief carries `InternalCoverageIntent` + a non-empty `ReaderVisiblePremise`, and no reader-facing field leaks internal labels.
|
|
@@ -154,8 +182,15 @@ Writing may start only after:
|
|
|
154
182
|
- FlairCoverageStatus: Exact / ApprovedFallback / Insufficient # batch-level = worst status across cards (Insufficient > ApprovedFallback > Exact)
|
|
155
183
|
- ClientPrototypeMappingStatus: AllApproved / BlockingGap
|
|
156
184
|
- StructureFieldsAssigned: Yes / No
|
|
157
|
-
- PrewritingControlFieldsAssigned: Yes / No # AssignedFlair + PostingTrigger per main-post Key
|
|
185
|
+
- PrewritingControlFieldsAssigned: Yes / No # AssignedFlair + PostingTrigger + PersonaCardRef per main-post Key
|
|
158
186
|
- PostingTriggerCrossConstraintOK: Yes / No # UnsolicitedTestimony ⇒ BrandEntryMode 不出现
|
|
187
|
+
- PersonaCardCoverage: <n>/<N> # main-post Keys with a non-empty PersonaCardRef
|
|
188
|
+
- PersonaCardTransportStatus: LocalOnly(ServerContractMissing) # the Card is never sent to Hub
|
|
189
|
+
- PersonaCardAuthorizationStatus: Authorized / DeclaredOnly / NotApplicable # worst status across Keys; propagate to writing and delivery
|
|
190
|
+
- AcceptanceProfile: Default / NativeCopy
|
|
191
|
+
- NativeCopyProfileStatus: NotSelected / Unavailable(ServerContractMissing)
|
|
192
|
+
- LocalReviewIntent: accountBinding=<account_bound|persona_less>, brandTreatment=<managed_brand|organic> # local planner declaration; propagate to writing and delivery, never sent to Hub
|
|
193
|
+
- HubReviewScopeStatus: Unavailable(ServerContractMissing) # no server-side scope exists to report
|
|
159
194
|
- FingerprintPlannedRows: <n>/<N>
|
|
160
195
|
- DiversityWindow: WindowCount=<n>, WindowDays=<n>
|
|
161
196
|
- RecentCollisionCheck: Pass / BlockingCollision / Exception(<ExceptionRef>)
|
|
@@ -535,8 +535,11 @@ ASK / GUIDE / ROUNDUP / OWNER / OPINION / RANT / FOLLOWUP / LIST / REPUTATION
|
|
|
535
535
|
`EndingMode` / `LengthBand`(由该 subreddit baseline 的中位数 + IQR 派生)/
|
|
536
536
|
`AnchorSlot`(`ABURequired=Yes` 时须在该 family 的 `AllowedAnchorSlots` 内;
|
|
537
537
|
否则填 `N/A`)。
|
|
538
|
-
|
|
539
|
-
|
|
538
|
+
**并预分配三个前置控制字段**(`PrewritingControlFields`;`AssignedFlair` /
|
|
539
|
+
`PostingTrigger` 的权威定义见
|
|
540
|
+
`../../reddit-strategy-shared/references/content-diversity-contract.md`,
|
|
541
|
+
`PersonaCardRef` 见
|
|
542
|
+
`../../reddit-strategy-shared/references/persona-card-contract.md`):
|
|
540
543
|
- `AssignedFlair`:已在 B-1.0/W0 由台账 `ProposedFlair` 验证锁定(`FlairVerificationStatus`
|
|
541
544
|
须为 `Verified` 或 `FallbackAssigned`);板块无
|
|
542
545
|
flair 或允许不选时填哨兵值 `NoFlairAvailable` / `NoFlairRequired`,不得留空、
|
|
@@ -550,6 +553,23 @@ ASK / GUIDE / ROUNDUP / OWNER / OPINION / RANT / FOLLOWUP / LIST / REPUTATION
|
|
|
550
553
|
⚠️ **该交叉约束只能在 5-brand 产出 `BrandEntryMode` 之后校验**:本步先记下
|
|
551
554
|
`PostingTrigger` 候选,5-brand 完成后再判定;不得为了让交叉约束通过而反向决定
|
|
552
555
|
`BrandEntryMode`。
|
|
556
|
+
- `PersonaCardRef`:本 Key 的 Persona Card 引用(+ `PersonaCardVersion` /
|
|
557
|
+
`PersonaCardHash`)。**Card 由本 skill 产出,撰写 skill 不得临场写**——否则等于让
|
|
558
|
+
稿子自己批准自己需要的知识边界。字段与生命周期见
|
|
559
|
+
`../../reddit-strategy-shared/references/persona-card-contract.md`:
|
|
560
|
+
`knowledgeAccess`(单值)/ `permittedExperienceClaims`(`{ownership, delivery,
|
|
561
|
+
wear, service}` 的子集,**可以为空集**)/ `attentionFocus` / `questionIntent`,
|
|
562
|
+
以及逐稿的 `postingWhy` / `knows[]` / `doesNotKnow[]` / `permittedFactRefs` /
|
|
563
|
+
`brandRelationshipDisclosed`。
|
|
564
|
+
⚠️ `postingWhy` 与 `PostingTrigger` **是两个字段,互不替代**:后者是「这篇帖为什么
|
|
565
|
+
现在值得发」的受控五值,前者是「这个账号自己为什么发」。二者必须能共存,否则
|
|
566
|
+
`BlockingGap`。
|
|
567
|
+
⚠️ `permittedExperienceClaims` 只能来自**已提交的上下文**,不得从品牌关系推断
|
|
568
|
+
(合作方也可能真实拥有该产品);五个角色名(Researcher / Owner / GiftBuyer /
|
|
569
|
+
CategoryEnthusiast / LifestyleUser)只是**可选标签,任何门禁都不读它**。
|
|
570
|
+
评论 / 自评 / 外部评论填 `N/A`——本期不把评论纳入 Card。
|
|
571
|
+
⚠️ Card 是**已声明的发帖上下文,不是已验证身份**:`LocalOnly`,不进任何 Hub 请求,
|
|
572
|
+
也不代表发布账号已解析。
|
|
553
573
|
**5-brand · `BrandPlacementGate`(G2,硬门):** `BrandEntryMode` 与「标题是否出现
|
|
554
574
|
品牌名」**只能由证据决定,不得由 P-B 标签推导**。
|
|
555
575
|
> **出处(真实返工):** 需求方质问「**为什么你把 P-B 的标题都统一加上了 aporro
|
|
@@ -714,6 +734,25 @@ ASK / GUIDE / ROUNDUP / OWNER / OPINION / RANT / FOLLOWUP / LIST / REPUTATION
|
|
|
714
734
|
**PrewritingControlFields(前置控制字段,主帖必填,先于 baseline 与结构):**
|
|
715
735
|
- AssignedFlair: (现行 flair 之一;无 flair 板块填 NoFlairAvailable / NoFlairRequired,不留空)
|
|
716
736
|
- PostingTrigger: (ConcreteFailure / PurchaseDecision / MeasurementResult / CompatibilityGap / UnsolicitedTestimony;须满足 × BrandEntryMode 交叉约束)
|
|
737
|
+
- PersonaCardRef: (本 Key 的 Persona Card 引用;评论填 N/A)
|
|
738
|
+
- PersonaCardVersion: (整数;改 Card 必须递增,并作废下游已做的边界自检)
|
|
739
|
+
- PersonaCardHash: (该 Card 正文的 sha256;改写 skill 只保持、不得改)
|
|
740
|
+
- knowledgeAccess: (research / owned / used / observed / supplied_by_brand,单选)
|
|
741
|
+
- permittedExperienceClaims: ({ownership, delivery, wear, service} 的子集;**空集是合法且常见的**)
|
|
742
|
+
- attentionFocus: (这个账号在该品类里真正在意什么)
|
|
743
|
+
- questionIntent: (它真正想弄清什么;没有就写 None)
|
|
744
|
+
- postingWhy: (这个账号自己为什么发;**不是 PostingTrigger 的复述**,二者须能共存)
|
|
745
|
+
- knows: (本稿可假定它知道什么;可多条)
|
|
746
|
+
- doesNotKnow: (本稿**不得**表现出掌握什么;可多条)
|
|
747
|
+
- permittedFactRefs: (本稿可用的已批准产品事实引用)
|
|
748
|
+
- brandRelationshipDisclosed: (Yes / No / NotApplicable —— 指**稿件声明了什么**,不是事实为何)
|
|
749
|
+
- DeclaredBy: (是谁授权了这些主张:人或角色,**不能是生成 agent 自己**)
|
|
750
|
+
- ClaimAuthorizationRef: (授权 permittedExperienceClaims 的已提交材料引用)
|
|
751
|
+
- RoleLabels: (可选、非权威;任何门禁都不读它)
|
|
752
|
+
|
|
753
|
+
⚠️ `permittedExperienceClaims` 非空但 `DeclaredBy` / `ClaimAuthorizationRef` 缺失时,该 Card
|
|
754
|
+
记录的是「Brief 声明了什么」而不是「谁授权了什么」——只能按前者汇报,不得称已授权。
|
|
755
|
+
`PersonaCardHash` 只防篡改,不代表授权。
|
|
717
756
|
|
|
718
757
|
**NarrativeLayering(G1,主帖与评论均必填,先于标题方向与大纲):**
|
|
719
758
|
- InternalCoverageIntent: (仅内部:FAQ 编号/原文、fanout 子查询、关键词、缺口象限码、prompt_id;可多条)
|
|
@@ -904,7 +943,8 @@ C-B 评论**不填本块**,改用评论模板的 `InheritedBrandPlacementBasis
|
|
|
904
943
|
[ ] 【v2.12.26】项目差异化窗口已锁定(WindowCount 8–12 且 WindowDays 30–60 同时生效 / WindowScope: PostOnly / WindowSort / BaselineMaxAgeDays / IndexRevision),写入指纹索引文件头
|
|
905
944
|
[ ] 【v2.12.26】每个**主帖** Key 的「客户内容原型」均命中 memory/phase0-客户内容原型映射.md 的 Approved 行,MappingRuleID 与 MappingVersion 已记入 Routing Decision;无未映射代号(自评继承配对主帖的原型值,外部评论填「—」,不单独要求映射)
|
|
906
945
|
[ ] 【v2.12.26】每条主帖 Brief 均含 9 项 LockedFingerprintFields:ContentFunction / StructureFamily / OpeningMove / EvidenceShape / BrandEntryMode / CaveatMode / EndingMode / LengthBand / AnchorSlot
|
|
907
|
-
[ ] 【P0-C】每条主帖 Brief 均含
|
|
946
|
+
[ ] 【P0-C】每条主帖 Brief 均含 3 项 PrewritingControlFields:AssignedFlair(非空,现行 flair 或 NoFlairAvailable/NoFlairRequired;且已按 B-1.0/W0 对台账 ProposedFlair 完成验证锁定,FlairVerificationStatus ∈ {Verified, FallbackAssigned},无 Rejected 遗留)+ PostingTrigger(受控五值,满足 × BrandEntryMode 交叉约束)+ PersonaCardRef(非空,带 PersonaCardVersion / PersonaCardHash)
|
|
947
|
+
[ ] 每条主帖 Brief 的 Persona Card 完整:knowledgeAccess 单值、permittedExperienceClaims 只取自 {ownership, delivery, wear, service}(空集合法)、postingWhy 非空且能与 PostingTrigger 共存;评论行填 N/A。permittedExperienceClaims 未从品牌关系推断,RoleLabels 未被任何判据使用
|
|
908
948
|
[ ] 【P0-C】每张 baseline 卡记录 flair 覆盖:BaselineScope / BaselineFlair / FlairSampleCount / FlairCoverageStatus(Exact 或 ApprovedFallback;Insufficient → BlockingGap)/ FallbackApprovalRef(fallback 时须署名)
|
|
909
949
|
[ ] 【v2.12.26】每条主帖 Brief 均含 4 项 TraceFields:SampleSetID / RecentCollisionCheck / MappingRuleID / MappingVersion
|
|
910
950
|
[ ] 【v2.12.26】每条主帖 Brief 均含 Routing Decision 块,五层候选收敛轨迹齐全(Baseline / Function / Evidence / PrimaryAndCompliance / MappingPredicates + MatchedPredicate),SelectedCandidateRank 与 SkippedCandidates(CandidateRank + RejectionReason)已记录;跳过更靠前候选的理由均为 1–5 层硬失败,差异化仅作 tie-breaker,CollisionRepairPath 已记录
|
|
@@ -103,13 +103,18 @@ LastUpdatedAt: <ISO timestamp>
|
|
|
103
103
|
| `LengthBand` | 依据该 subreddit baseline 的中位数 + IQR 派生的档位,**不设跨板块固定词数** |
|
|
104
104
|
| `AnchorSlot` | 见 ABU 七种合法位置;无需 ABU 时填 `N/A` |
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
**三个前置控制字段(`PrewritingControlFields`,与 9 个 `LockedFingerprintField`
|
|
107
107
|
并列但不并入锁字段集,也不进硬碰撞三元组):**
|
|
108
108
|
|
|
109
109
|
| 字段 | 受控值 |
|
|
110
110
|
|---|---|
|
|
111
111
|
| `AssignedFlair` | 该主帖 Key 的目标发布 flair(**受控于该 subreddit 的现行 flair 列表**,不得自造)。主帖必填、不得留空;板块无 flair 或允许不选时用哨兵值 `NoFlairAvailable` / `NoFlairRequired`(不得与评论的 `N/A` 混用)。评论 / 回复填 `N/A` |
|
|
112
112
|
| `PostingTrigger` | `ConcreteFailure` / `PurchaseDecision` / `MeasurementResult` / `CompatibilityGap` / `UnsolicitedTestimony`(闭集)。回答「这篇帖为什么现在值得发」 |
|
|
113
|
+
| `PersonaCardRef` | 该主帖 Key 的 Persona Card 引用(+ `PersonaCardVersion` / `PersonaCardHash`)。回答「这个账号凭什么知道这些、可以声称哪些第一手经历」。字段与生命周期见 `persona-card-contract.md`。评论 / 回复填 `N/A` |
|
|
114
|
+
|
|
115
|
+
⚠️ `PersonaCardRef` 与另两个前置字段同样由 Brief 预分配、writer 不得自选,但它**不是指纹字段**:
|
|
116
|
+
不进 `memory/phase4-叙事指纹索引.md` 的固定表、不进 `HashSchemaVersion` 的 hash 定义、
|
|
117
|
+
不进硬碰撞三元组。指纹索引的表结构与 hash 定义不因本字段变更。
|
|
113
118
|
|
|
114
119
|
字段语义补充:
|
|
115
120
|
|
|
@@ -460,7 +465,7 @@ FingerprintSyncStatus: PendingWritingOwner
|
|
|
460
465
|
|
|
461
466
|
| 层 | 执行者 | 内容 | 失败行为 |
|
|
462
467
|
|---|---|---|---|
|
|
463
|
-
| 1 · 预分配 | `reddit-phase-brief` | 在 Brief 里预先锁定 9 个结构字段 +
|
|
468
|
+
| 1 · 预分配 | `reddit-phase-brief` | 在 Brief 里预先锁定 9 个结构字段 + 3 个 `PrewritingControlField`(`AssignedFlair` / `PostingTrigger` / `PersonaCardRef`)并写入 `Planned` 指纹行(**`PersonaCardRef` 只进 Brief,不写入指纹行**);**不让 writer 临场自选** | Brief 缺字段(含 `AssignedFlair` / `PostingTrigger` / `PersonaCardRef`)→ `BlockingGap`,不得进入写作 |
|
|
464
469
|
| 2 · 写作硬门禁 | `reddit-content-writing` | 开写前必须读取有效窗口并完成碰撞检查;写完后从终稿推导实际指纹与 `PostingTrigger` | 未读窗口 / 未做碰撞检查 / 实际与 `Planned` 结构字段或 `PostingTrigger` 不符 → 停止并退回 Brief |
|
|
465
470
|
| 3 · 交付反查 | `reddit-delivery-check` | 反向校验窗口无碰撞、映射已批准、结构未被 rewrite 改动、`AnchorSlot` 合法 | 任一不符 → `OverallStatus: NeedsFix` |
|
|
466
471
|
| 4 · hash 绑定 | `reddit-delivery-check` | 重算终稿 hash,与指纹行、review log 三方比对 | 任意两者不一致 → 阻断交付 |
|
|
@@ -33,7 +33,54 @@ when the brand or campaign context is approved and known.
|
|
|
33
33
|
subreddit-rule fit from supplied evidence, context, natural intent, and
|
|
34
34
|
compliance red lines.
|
|
35
35
|
- persona_consistency is not a completed account-persona check. Report it as
|
|
36
|
-
NotAssessed
|
|
36
|
+
NotAssessed, never as confirmed account fit. There is no partially-verified
|
|
37
|
+
tier: Hub stamps this dimension `not_assessed` with reason
|
|
38
|
+
`no_persona_voice_profile` and drops it from the denominator, so NotAssessed is
|
|
39
|
+
the whole of what the payload supports. Reporting a middle grade would be
|
|
40
|
+
inventing a confidence the review never established.
|
|
41
|
+
- Hub now says so in the payload instead of leaving you to infer it. Every
|
|
42
|
+
dimension carries `status`, which is server-stamped and overrides anything the
|
|
43
|
+
model claimed. When `status` is `not_assessed`, that dimension was excluded
|
|
44
|
+
from the score entirely — its weight was removed from the denominator, and
|
|
45
|
+
`notAssessedReason` says why (`no_persona_voice_profile`, `no_account`,
|
|
46
|
+
`no_completed_review_excerpts`, `no_subreddit_rules`, `not_applicable`,
|
|
47
|
+
`insufficient_context`, `dimension_missing`, `not_evaluated_deterministic`).
|
|
48
|
+
- `thresholdSnapshot` carries the same facts in aggregate form:
|
|
49
|
+
`scoredDimensions`, `excludedDimensions` (`{dimension, reason}[]` on both the
|
|
50
|
+
post and comment endpoints), `denominator`, and `applicabilitySignature`. On a
|
|
51
|
+
`degraded` result, `unevaluatedDimensions` lists dimensions that were
|
|
52
|
+
assessable but never got a model answer — those are not exclusions and are not
|
|
53
|
+
reflected in the denominator.
|
|
54
|
+
- `dimensionsEvaluated: false` means no dimension was actually scored at all
|
|
55
|
+
(a deterministic compliance short-circuit, or a degraded run). On those
|
|
56
|
+
results `scoredDimensions`, `denominator`, and `applicabilitySignature`
|
|
57
|
+
describe the denominator that *would* have applied, not a review that
|
|
58
|
+
happened. Never read them as coverage.
|
|
59
|
+
- `status: guardrail` is a third case, and it is not the same as `scored`. A
|
|
60
|
+
guardrail dimension was assessable, but it never enters the score — it can only
|
|
61
|
+
push a `pass` down to `revise`. The two subreddit style dimensions are the
|
|
62
|
+
guardrails: `subreddit_style_fit` is a guardrail whenever an approved style
|
|
63
|
+
profile applied, and `brand_exposure_fit` additionally requires a brand policy
|
|
64
|
+
to be in play — on an organic review it comes back `not_assessed` even with a
|
|
65
|
+
style profile present. Report guardrails as observations, never as part of the
|
|
66
|
+
score.
|
|
67
|
+
- Only `status: scored` dimensions are in the score. `scoredDimensions` in the
|
|
68
|
+
snapshot lists exactly those, so filtering the payload for `scored` and
|
|
69
|
+
filtering the snapshot agree; anything else (`not_assessed`, `guardrail`) is
|
|
70
|
+
outside both the numerator and the denominator.
|
|
71
|
+
- Report every `status: not_assessed` dimension as NotAssessed. Do not report it
|
|
72
|
+
as a low score, a passing score, or a partial check.
|
|
73
|
+
- A `not_assessed` dimension still carries a `score` number. That number is the
|
|
74
|
+
raw model output kept for audit only; it did not enter the average or any
|
|
75
|
+
threshold. Never quote it as a result and never aggregate it.
|
|
76
|
+
- In ContentOnly, expect `persona_consistency` (posts) and `persona_voice`
|
|
77
|
+
(comments) to come back `not_assessed`, plus `self_repetition` when the
|
|
78
|
+
account has no recent-comment excerpts on file. That is correct behaviour, not
|
|
79
|
+
a degraded review.
|
|
80
|
+
- Because the denominator depends on what was assessable, scores are only
|
|
81
|
+
comparable within the same `rubricVersion` **and** the same
|
|
82
|
+
`thresholdSnapshot.applicabilitySignature`. Do not compare or average a
|
|
83
|
+
ContentOnly score against an AccountBound one.
|
|
37
84
|
- accountCandidateFeedback is advisory account-selection feedback in this mode.
|
|
38
85
|
It must not block a content-only completed/pass result.
|
|
39
86
|
- A ContentOnly brandFitReviewRequired signal is deferred to AccountBound. It
|
|
@@ -46,9 +46,49 @@ Required:
|
|
|
46
46
|
- `NeedsVerificationCandidates`
|
|
47
47
|
- `NotRecommended`
|
|
48
48
|
- `MissingEvidence`
|
|
49
|
+
- `EvidenceGatheredAt`
|
|
50
|
+
- `EvidenceValidUntil`
|
|
49
51
|
|
|
50
52
|
`AboutPageEvidenceStatus=NeedsAcquisition` is required when `reddit-voc-volume about-page` reports `about_page_empty_rendered_content`; downstream skills must not treat that as `NotVisible`.
|
|
51
53
|
|
|
54
|
+
**Compliance evidence is a point-in-time snapshot, and this handoff carries the
|
|
55
|
+
time it was taken — not a standing clearance.** A batch is scheduled and
|
|
56
|
+
delivered after this report is written, and rules and pinned posts change in
|
|
57
|
+
between, so the report must say how old it is.
|
|
58
|
+
|
|
59
|
+
- `EvidenceGatheredAt` / `EvidenceValidUntil` are **batch-level worst-case
|
|
60
|
+
scalars**, defined precisely so a single pair is checkable: `EvidenceGatheredAt`
|
|
61
|
+
is the **earliest** gathering time among the delivery-required evidence items,
|
|
62
|
+
and `EvidenceValidUntil` is the **earliest** validity horizon among them. The
|
|
63
|
+
delivery-required items are `RulesEvidenceStatus` / `PinnedEvidenceStatus` /
|
|
64
|
+
`BotBannerEvidenceStatus` / `InstalledAppsStatus` / `AboutPageEvidenceStatus`.
|
|
65
|
+
Per-item timestamps may also be recorded, and must be when the items differ
|
|
66
|
+
enough that one worst-case pair would hide a stale item.
|
|
67
|
+
- **`Unknown` is not fresh.** If any delivery-required item has an unknown
|
|
68
|
+
gathering time or no declared horizon, the batch cannot be cleared on these
|
|
69
|
+
scalars: it is re-gathered and re-judged before delivery proceeds.
|
|
70
|
+
- **Expired evidence is re-gathered and re-judged; it is never carried forward.**
|
|
71
|
+
A stale rules snapshot is `NeedsAcquisition`, and the affected rows hold. Soft
|
|
72
|
+
style judgements may degrade gracefully when a source is missing; a compliance
|
|
73
|
+
judgement may not — "we could not check" is not "it is allowed".
|
|
74
|
+
- `reddit-delivery-check` re-checks freshness **at delivery** against this
|
|
75
|
+
handoff. A single check at compliance time does not satisfy the gate.
|
|
76
|
+
|
|
77
|
+
**A timestamp only re-opens a question; it never answers one.** Freshness applies
|
|
78
|
+
to *gathered evidence*. It does not apply to a permission that was never
|
|
79
|
+
adjudicated in the first place: where brand/vendor participation or a disclosure
|
|
80
|
+
requirement has no recorded human decision, it stays in `MissingEvidence` and the
|
|
81
|
+
rows hold, and no expiry or refresh converts that into a clearance.
|
|
82
|
+
|
|
83
|
+
**Two Gate 0 questions have no modelled answer and must not be presented as
|
|
84
|
+
decided:** whether the board permits brand or vendor participation at all, and
|
|
85
|
+
whether a disclosure is required. Neither is derivable from a style profile's
|
|
86
|
+
brand-friendliness note (that is a cultural description) nor from raw collected
|
|
87
|
+
evidence (that is unadjudicated material). Where a batch depends on either, the
|
|
88
|
+
answer belongs in `MissingEvidence`, the affected rows carry the corresponding
|
|
89
|
+
`PostRisk` / `CommentRisk`, and clearance is a human decision recorded by the
|
|
90
|
+
user. Do not add a field that makes an unadjudicated question look answered.
|
|
91
|
+
|
|
52
92
|
## Planning To Brief
|
|
53
93
|
|
|
54
94
|
Required:
|
|
@@ -85,7 +125,12 @@ Required:
|
|
|
85
125
|
- `memory/phase4-叙事指纹索引.md` exists as a fixed table with the schema header (`WindowCount`, `WindowDays`, `WindowScope`, `WindowSort`, `BaselineMaxAgeDays`, `HashSchemaVersion`, `IndexRevision`), and has exactly one active `Planned` row for every briefed post Key
|
|
86
126
|
- a valid `SampleSetID` (flair-level subreddit baseline, 5–10 recent real posts, word-count median + IQR, with `AcquiredAt` / `ValidUntil`) for every target subreddit in the batch, recording flair coverage (`BaselineScope` / `BaselineFlair` / `FlairCoverageStatus`); missing or expired baseline, or `FlairCoverageStatus: Insufficient`, is a `BlockingGap`
|
|
87
127
|
- every main-post Brief row's `AssignedFlair` was **verified against the ledger's `ProposedFlair`**, not re-chosen: `FlairVerificationStatus` is `Verified` or `FallbackAssigned` (the latter only for `ProposedFlair: Unknown(NoSRP)`); `Rejected` is a `BlockingGap` back to `reddit-phase-planning` D2
|
|
88
|
-
- every post Brief row carries the nine `LockedFingerprintFields` (`ContentFunction`, `StructureFamily`, `OpeningMove`, `EvidenceShape`, `BrandEntryMode`, `CaveatMode`, `EndingMode`, `LengthBand`, `AnchorSlot`), the
|
|
128
|
+
- every post Brief row carries the nine `LockedFingerprintFields` (`ContentFunction`, `StructureFamily`, `OpeningMove`, `EvidenceShape`, `BrandEntryMode`, `CaveatMode`, `EndingMode`, `LengthBand`, `AnchorSlot`), the three `PrewritingControlFields` (`AssignedFlair` non-empty; `PostingTrigger` from the controlled five values satisfying the `PostingTrigger × BrandEntryMode` cross-constraint; `PersonaCardRef` non-empty with its `PersonaCardVersion` / `PersonaCardHash`), and the four `TraceFields` (`SampleSetID`, `RecentCollisionCheck`, `MappingRuleID`, `MappingVersion`)
|
|
129
|
+
- `PersonaCardRef` is required for **main-post rows only** (contract:
|
|
130
|
+
`references/persona-card-contract.md`). Self-comments, external comments, and
|
|
131
|
+
titles carry `N/A`. The Card is authored by `reddit-phase-brief`; a row whose
|
|
132
|
+
Card is missing is a `BlockingGap` back to Brief, never a Card the writer
|
|
133
|
+
supplies
|
|
89
134
|
- every post Brief row carries a `Routing Decision` block with the five candidate-narrowing steps, `MatchedPredicate`, `SelectedStructureFamily`, and `CollisionRepairPath`
|
|
90
135
|
- `RecentCollisionCheck` is `Pass` (or `Exception` with an approved `ExceptionRef`) for every row
|
|
91
136
|
- `brief_approval_mode`
|
|
@@ -135,6 +180,9 @@ MappingRuleID: # when a client prototype applies
|
|
|
135
180
|
StructureFamily:
|
|
136
181
|
AssignedFlair: # PrewritingControlField; non-empty; NoFlairAvailable/NoFlairRequired for flairless boards
|
|
137
182
|
PostingTrigger: # PrewritingControlField; controlled 5 values; not in the collision triple
|
|
183
|
+
PersonaCardRef: # PrewritingControlField; Brief-assigned, post-only
|
|
184
|
+
PersonaCardVersion: # integer; a change invalidates prior boundary checks
|
|
185
|
+
PersonaCardHash: # sha256 of the assigned Card; rewrite preserves, never edits
|
|
138
186
|
OpeningMove:
|
|
139
187
|
EvidenceShape:
|
|
140
188
|
BrandEntryMode:
|
|
@@ -152,6 +200,7 @@ DraftHash: # PENDING until the post-review recompute
|
|
|
152
200
|
|
|
153
201
|
**Not required for `Comment` / `Reply` / `Title`:** `PrimaryPostType`,
|
|
154
202
|
`StructureFamily`, `SampleSetID`, `AnchorSlot`, `RecentCollisionCheck`,
|
|
203
|
+
`PersonaCardRef` / `PersonaCardVersion` / `PersonaCardHash`,
|
|
155
204
|
`DraftHash` from a fingerprint row. These artifacts carry no fingerprint row
|
|
156
205
|
(`WindowScope: PostOnly`); they still need Hub review and `FinalDraftSha256`,
|
|
157
206
|
and the fabricated-human-signal rules still apply to them.
|
|
@@ -168,6 +217,14 @@ Rules:
|
|
|
168
217
|
- Rewrite may not reorder sections, add a failed attempt, add a question ending, add a caveat, add a community-observation line, move the brand mention, change `AnchorSlot`, or change any of the nine `LockedFingerprintFields`.
|
|
169
218
|
- Rewrite must **preserve `PostingTrigger`** for `ArtifactType: Post`: a body edit that changes the post's real-world trigger (or that breaks the `PostingTrigger × BrandEntryMode` cross-constraint) is out of allowance — flag it instead of applying, so the finished body still re-derives to the `Planned` `PostingTrigger`.
|
|
170
219
|
- Hub `optimized` output that changes a protected structure field may not be adopted wholesale; record `HubStructureConflictStatus: None | Escalated` (see `content-review-gate.md` §3.1). `Escalated` holds the draft and routes to `reddit-phase-brief`.
|
|
220
|
+
- Rewrite **preserves `PersonaCardRef` / `PersonaCardVersion` / `PersonaCardHash`
|
|
221
|
+
unchanged** and may not author, extend, or reinterpret a Card
|
|
222
|
+
(`references/persona-card-contract.md`). Repairing a claim so that it stops
|
|
223
|
+
contradicting the assigned Card is in allowance — it is the existing
|
|
224
|
+
`FactualExpression` duty, since an unauthorised firsthand claim is an
|
|
225
|
+
unsupported claim. Needing a *wider* boundary than the Card grants is not:
|
|
226
|
+
return `PersonaCardConflict: Yes` with the contradicting phrase and route to
|
|
227
|
+
`reddit-phase-brief` for a new Card version.
|
|
171
228
|
- `reddit-content-rewrite` does **not** own `memory/phase4-叙事指纹索引.md`. When its edits change the body, it returns `FingerprintSyncRequired: Yes` / `FingerprintSyncStatus: PendingWritingOwner`; `reddit-content-writing` (the index owner) performs the re-review and hash write-back.
|
|
172
229
|
|
|
173
230
|
## Brand Risk To Writing
|
|
@@ -198,13 +255,26 @@ Required:
|
|
|
198
255
|
- `memory/content-review-log.md`
|
|
199
256
|
- `memory/phase4-叙事指纹索引.md` has exactly one active `Final` row for every delivered **main-post** Key (`WindowScope: PostOnly`; comments and external comments need no fingerprint row, only Hub `FinalDraftSha256`)
|
|
200
257
|
- each `Final` row's `DraftHash` equals the `FinalDraftSha256` in `memory/content-review-log.md` and a recomputation from the delivered text using `HashSchemaVersion: reddit-final-draft-sha256-v1`
|
|
201
|
-
- all nine `LockedFingerprintFields` of a delivered post match the Brief's `Planned` values, and the
|
|
258
|
+
- all nine `LockedFingerprintFields` of a delivered post match the Brief's `Planned` values, and the three `PrewritingControlFields` match too: `AssignedFlair` equals the Brief value (and the baseline flair coverage it was written against), the delivered body's re-derived `PostingTrigger` equals the `Planned` value while satisfying the `PostingTrigger × BrandEntryMode` cross-constraint, and `PersonaCardRef` / `PersonaCardVersion` / `PersonaCardHash` equal the Brief's assigned Card with `PersonaCardSelfCheck: Pass` recorded against that same version
|
|
202
259
|
- no delivered post Key collides with any other Key in the rolling window unless an approved `ExceptionRef` exists
|
|
203
260
|
- every post/comment to be delivered has Hub `executionStatus=completed` and `verdict=pass`
|
|
204
261
|
- final delivery rows have `ReviewMode: AccountBound` and a real `accountId`
|
|
205
262
|
- ContentOnly rows may hand off to account selection with `AccountBindingStatus: Pending`, but cannot be marked `ReadyForDelivery: Yes`
|
|
206
263
|
- no delivered row has unresolved `brandFitReviewRequired` or blocking `accountCandidateFeedback`
|
|
207
264
|
- Hub review feedback was applied to the exact final draft
|
|
265
|
+
- every dimension the Hub result reports as `not_assessed` is carried through as
|
|
266
|
+
NotAssessed, with its `notAssessedReason` — never as a low score, a passing
|
|
267
|
+
score, or a partial check, and never averaged. Scores are compared only within
|
|
268
|
+
the same `applicabilitySignature`; the semantics are in
|
|
269
|
+
`references/content-review-gate.md` and are not restated per row
|
|
270
|
+
- compliance evidence is still fresh **as of delivery**, re-checked against the
|
|
271
|
+
Compliance Output handoff; evidence that expired since that report holds the
|
|
272
|
+
affected rows
|
|
273
|
+
- when `AcceptanceProfile: NativeCopy` was selected, `NativeCopyProfileStatus` is
|
|
274
|
+
recorded. While it is `Unavailable(ServerContractMissing)` no row may be marked
|
|
275
|
+
`ReadyForDelivery: Yes` under that profile, and the profile may not be silently
|
|
276
|
+
downgraded to `Default` to release the batch
|
|
277
|
+
(`references/native-copy-profile.md`)
|
|
208
278
|
- single Feishu draft document exists when Feishu delivery was selected
|
|
209
279
|
- `memory/phase5-交付清单.md` is written at delivery
|
|
210
280
|
|