@antoneeo/mkt-agentic-sdlc-skill 0.8.0 → 0.10.0

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 CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.0] - 2026-09-06
4
+
5
+ ### Added
6
+ - **F-046: the protocol reaches every turn (`remind`, UserPromptSubmit, wired by
7
+ default).** `orient` fires once at session start and decays; a session that
8
+ never loads the skill never had the protocol at all. Measured 2026-09-06: a
9
+ release ran against a `GUIDE_release.md` the session never consulted, because
10
+ `SKILL.md` -- where the duty is written -- had not been read. `remind` prints
11
+ ONE constant line at every prompt, and its first duty is that one: decide
12
+ whether the skill governs the work in front of you and, if it does, LOAD it
13
+ once. The carrier is kb's F-041 item B, LIFTED into the shared spine rather
14
+ than duplicated, so all three lenses share one machine; each lens declares its
15
+ own line in `set_profile(remind_line=...)`. Installing the package wires it
16
+ machine-wide on Claude Code alongside the orientation hook, never instead of
17
+ it; removing the hook entry is a standing opt-out. Per-turn cost disclosed and
18
+ accepted by the owner (`vision/rulings.md` r19, the "no ceremony ratchet"
19
+ Non-Goal's second door), and bounded to stay flat by a contract the new shared
20
+ `scripts/test_remind.py` guards: constant text, zero reads, one line under 500
21
+ characters, argv ignored whole, always exit 0.
22
+
23
+ ### Changed
24
+ - **`guides.md` said something false about code guides.** It claimed `stale`
25
+ works unchanged for `source_kind: code` because "the code-excerpt snapshot
26
+ drifts when the code changes". A snapshot is a frozen copy and never drifts:
27
+ freshness is NOT mechanized there, and the duty is the author's twice -- when
28
+ you MODIFY code a comprehension guide describes, and when you merely READ one
29
+ before trusting it. Measured 2026-09-06: the guided module had gained 388
30
+ lines and lost 71 across four commits with `stale` silent.
31
+
32
+ ## [0.9.0] - 2026-09-05
33
+
34
+ Spine-only release: `review.md` updated byte-identical across the family
35
+ (authored in the code lens, code 1.31.0).
36
+
37
+ ### Changed
38
+ - **The form budget belongs to the FIRST draft (shared spine `review.md`).**
39
+ The lean operative form is the first draft's form, not the round-3 rescue:
40
+ the review request states the artifact's line count against a budget the
41
+ author fixed before drafting, and an artifact over budget is the reviewer's
42
+ first finding, before content. This replaces "rewrite lean at the latest
43
+ before round 3", which paid for the form only after two rounds had already
44
+ been spent reading a bloated artifact. The ~1,250 -> ~500 line measurement
45
+ already in the file is the budget's anchor.
46
+
3
47
  ## [0.8.0] - 2026-09-01
4
48
 
5
49
  Spine-only release: `review.md` updated byte-identical across the family
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mkt-agentic-sdlc",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Evidence-First marketing planning protocol with engagement triage, SOSTAC workflow, evidence ledger and optional devPNT integration.",
5
5
  "author": "Antonio Pinto (https://github.com/Antoneeo)"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antoneeo/mkt-agentic-sdlc-skill",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Evidence-First marketing planning protocol for Claude Code, Gemini CLI, Google Antigravity and Codex: engagement triage, SOSTAC workflow, evidence ledger, mechanical validator and optional devPNT integration.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -157,6 +157,46 @@ Gemini CLI — wire the same command into its startup-hook mechanism if present;
157
157
  - **Hybrid/devPNT projects**: add `--hybrid` — the hook then appends a one-line pointer to run `devpnt_mcp_get_bootstrap` for the Master Plan / Knowledge Layer and does not replicate them; the filesystem orientation (router + handoff + README) still emits.
158
158
  - Like the CI gate (§2), if you copied the validator into the repo, the hook references that copy — keep both files current when you update the skill.
159
159
 
160
+ ### Per-turn reminder: `remind` (UserPromptSubmit -- wired by default)
161
+
162
+ `orient` fires once, at session start; a long session drifts after it, and a session
163
+ that never loads the skill never had the protocol at all. Measured (F-046, 2026-09-06):
164
+ a release ran against a `GUIDE_release.md` the session never consulted, because
165
+ `SKILL.md` -- where the duty is written -- had not been read. `mkt_check.py remind` prints
166
+ ONE constant line at every prompt, and its first duty is that one: decide whether the
167
+ skill governs the work in front of you and, if it does, load it once.
168
+
169
+ Cost: 437 characters, ~74 tokens per prompt, paid on every turn including
170
+ trivial ones. That is admissible only because the owner accepted it explicitly
171
+ (`ai_docs/vision/rulings.md` r19, the "no ceremony ratchet" Non-Goal's second door) --
172
+ and the acceptance was for a FLAT cost. Hence the contract, guarded by
173
+ `scripts/test_remind.py`: the line is **constant and reads nothing** (so nothing repo-
174
+ or session-controlled rides into the agent's context through it, and the cost cannot
175
+ grow with the session), it is **one line under 500 characters**, and it **can never
176
+ break a prompt** (argv ignored whole, always exit 0). A trivial turn pays a yes/no
177
+ judgement, never a load.
178
+
179
+ Installing the package wires it machine-wide on Claude Code, alongside the orientation
180
+ hook and never instead of it; **removing that hook entry is a standing opt-out.** To
181
+ wire it by hand:
182
+
183
+ ```json
184
+ {
185
+ "hooks": {
186
+ "UserPromptSubmit": [
187
+ {
188
+ "hooks": [
189
+ {
190
+ "type": "command",
191
+ "command": "python \"C:\Users\<user>\.claude\skills\mkt-agentic-sdlc\scripts\mkt_check.py\" remind"
192
+ }
193
+ ]
194
+ }
195
+ ]
196
+ }
197
+ }
198
+ ```
199
+
160
200
  ## 5. Skill eval battery (release gate)
161
201
 
162
202
  **Skill development only.** `test_*.py` and `evals/` are deliberately absent from the npm `files` allowlist — they never reach an installed consumer, so this section applies to whoever builds the skill, not to a project that uses it. (Consumers get `mkt_check.py` + `sdlc_core.py`; §1–§4 are theirs.)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mkt-agentic-sdlc
3
- version: 0.8.0
3
+ version: 0.10.0
4
4
  description: Evidence-First marketing planning protocol with engagement triage, SOSTAC workflow, evidence ledger, adversarial CMO review, a complete Standalone mode and optional symbiosis with devPNT. Use for marketing plans, go-to-market strategy, campaign planning and market research.
5
5
  author: Antonio Pinto (https://github.com/Antoneeo)
6
6
  copyright: (c) 2026 Antonio Pinto
@@ -60,6 +60,10 @@ already cover how to do this well?*
60
60
  match was not targeted (T7). Two guides are legitimate only when they cover
61
61
  distinct concerns — typically an operative guide plus the comprehension map of
62
62
  the component being touched; three is a smell, not a thorough lookup.
63
+ - **Check a `code` guide against the code before you trust it.** No check can do
64
+ this for you (§6): the reader is the only one who sees that the map and the
65
+ component have parted. A divergence you observe is yours to close in the same
66
+ unit of work.
63
67
  - **Under subagent dispatch** the consult happens at plan-authoring time (the
64
68
  orchestrator populates each task's `guides` field); a context-free subagent
65
69
  reads the pointers it was handed and does not run its own router lookup. See
@@ -271,17 +275,17 @@ state it explicitly when handing off a newly created guide.
271
275
 
272
276
  ## 6. Maintenance
273
277
 
274
- - **Source changed**: create a new snapshot (new hash), regenerate the guide
275
- from it (new `source_hash`), and if the guide is replacing a prior guide
276
- rather than updating in place, mark the old one `status: SUPERSEDED`.
277
- - **`stale` flags hash drift**: `sdlc_check.py stale` (also under `--hybrid`)
278
- compares each guide's recorded `source_hash` against the live snapshot file
279
- and reports `[stale]` when they diverge — that is the signal to regenerate,
280
- not a manual freshness check.
281
- - **`source_kind: code` freshness**: `stale` works unchanged (the code-excerpt
282
- snapshot drifts when the code changes → regenerate). ADDITIONALLY, when you modify
283
- the code a comprehension guide describes, refresh that guide in the SAME closure
284
- (docs travel with the code) — do not wait for `stale` to catch it. A stale
285
- comprehension guide is a confident-wrong map, worse than none.
278
+ - **Source changed**: new snapshot, regenerate the guide from it (new
279
+ `source_hash`); a guide REPLACING another is `status: SUPERSEDED` on the old
280
+ one. `sdlc_check.py stale` (also under `--hybrid`) reports `[stale]` when a
281
+ guide's recorded hash and its snapshot file diverge — the signal to regenerate.
282
+ - **`source_kind: code` freshness is NOT mechanized.** `stale` compares the guide
283
+ to its snapshot, and a code snapshot is a frozen copy — so a map whose component
284
+ was rewritten reports FRESH forever (measured 2026-09-06: +388/-71 lines in the
285
+ guided module, `stale` silent). The duty is therefore the author's, twice: when
286
+ you MODIFY code a comprehension guide describes, refresh the guide in the same
287
+ closure (docs travel with the code); when you merely READ one, check it against
288
+ the code before trusting it. Too large to absorb → mark it `SUPERSEDED` rather
289
+ than leave a confident-wrong map badged CURRENT, which is worse than none.
286
290
  - **Agent-global KB guides** use the same pipeline and validator via
287
291
  `--root ~/.agentic-sdlc`; freshness via the same `stale` engine.
@@ -98,10 +98,12 @@ unfalsifiable, the same defect as an unnamed EXISTS or a faked router verdict.
98
98
  own findings and can make the cap unreachable honestly. (1) *Patch-on-patch*:
99
99
  each round's fixes are grafted onto the previous text until stale counts,
100
100
  duplicated sections and internal contradictions are findings the revisions
101
- themselves created. When a round's findings are of that artifact-consistency
102
- class — and at the latest before a round-3 attempt — rewrite the artifact lean
103
- and operative against the checklist instead of patching again; a from-scratch
104
- rewrite is cheaper than a burned round. (2) *Revision narrative in the
101
+ themselves created. The lean operative form is the FIRST draft's form, not the
102
+ round-3 rescue: the review request states the artifact's line count against a
103
+ budget the author fixed before drafting (default: the measured figure below), and
104
+ an artifact over budget is the reviewer's first finding, before content. When a
105
+ round's findings are of that artifact-consistency class, rewrite lean instead of
106
+ patching again. (2) *Revision narrative in the
105
107
  artifact*: the artifact under review carries its own review archaeology
106
108
  ("v1.2 fixed…", "as the round-2 review noted…"). An artifact states what IS,
107
109
  once; how it got there lives in the REVIEW_LOG row's notes, never in the
@@ -69,6 +69,14 @@ sdlc_core.set_profile(
69
69
  # marketing overlay
70
70
  "subagent_dispatch", "question_discipline",
71
71
  ),
72
+ remind_line=(
73
+ "first decide if this skill governs the work; if yes, LOAD it once -- an "
74
+ "unread protocol cannot be applied. Declare the engagement level and the "
75
+ "guide-router verdict before acting. Every claim about the market, the "
76
+ "audience or a competitor carries its evidence -- an unsourced assertion "
77
+ "is a finding, not a premise. Replay governing text -- a brief, a "
78
+ "positioning line, a template -- against the case that motivated it."
79
+ ),
72
80
  # The strategy is reviewed before any tactic is executed: nine phases, but the
73
81
  # ordering the gate cares about is the same one every domain owes.
74
82
  design_gate_between=("### 6. Strategy", "### 8. Action"),
@@ -268,16 +268,18 @@ _PROFILE = {
268
268
  "support_files": (),
269
269
  "capabilities": frozenset(),
270
270
  "design_gate_between": (),
271
+ "remind_line": "",
271
272
  }
272
273
 
273
274
 
274
275
  def set_profile(skill_name, support_files=(), capabilities=(), unit_noun="feature",
275
- design_gate_between=()):
276
+ design_gate_between=(), remind_line=""):
276
277
  _PROFILE.update(skill_name=skill_name,
277
278
  unit_noun=unit_noun,
278
279
  support_files=tuple(support_files),
279
280
  capabilities=frozenset(capabilities),
280
- design_gate_between=tuple(design_gate_between))
281
+ design_gate_between=tuple(design_gate_between),
282
+ remind_line=remind_line)
281
283
 
282
284
 
283
285
  def profile():
@@ -1796,6 +1798,43 @@ def cmd_check(root, strict=False, hybrid=False):
1796
1798
 
1797
1799
  # --------------------------------------------------------------------- gate
1798
1800
 
1801
+ # --- UserPromptSubmit reminder hook (F-046) --------------------------------
1802
+ # `orient` fires once per session and decays; a session that never invokes the
1803
+ # skill never had the protocol at all. Measured 2026-09-06: a release ran in
1804
+ # this repo against a `GUIDE_release.md` the session never consulted, because
1805
+ # SKILL.md -- where the duty is written -- had not been read.
1806
+ #
1807
+ # The carrier is kb's (F-041 item B), lifted here so all three lenses share one
1808
+ # machine instead of two that do the same thing. Its contract is kept: the line
1809
+ # is CONSTANT and reads NOTHING -- nothing repo- or session-controlled may ride
1810
+ # text injected every turn, and zero reads is what keeps the per-turn cost flat.
1811
+ # What changed is the payload's first duty and the wiring. First duty: decide
1812
+ # whether the skill governs this work and, if it does, read it once -- an
1813
+ # unread protocol cannot be applied, which is the failure above. Wiring: a
1814
+ # default instead of opt-in, on the owner's explicit acceptance of the per-turn
1815
+ # cost (`vision/rulings.md` r19; the "no ceremony ratchet" Non-Goal admits a
1816
+ # never-zero cost by that acceptance, and a trivial turn pays a yes/no
1817
+ # judgement, never a load).
1818
+
1819
+ REMIND_FALLBACK = (
1820
+ "decide whether the {skill} skill governs this work; if it does, load it "
1821
+ "once before acting -- an unread protocol cannot be applied."
1822
+ )
1823
+
1824
+
1825
+ def cmd_remind(args):
1826
+ """UserPromptSubmit hook: print the lens's constant per-turn line. Always 0.
1827
+
1828
+ argv is ignored whole and nothing is read: a hook surface must not fail, and
1829
+ a flag typo must not cost a turn's context."""
1830
+ try:
1831
+ prof = profile()
1832
+ line = prof.get("remind_line") or REMIND_FALLBACK.format(skill=prof["skill_name"])
1833
+ print(f"{prof['skill_name']}: {line}")
1834
+ except Exception:
1835
+ pass
1836
+ return 0
1837
+
1799
1838
  def cmd_gate(args):
1800
1839
  file_path = args.file or ""
1801
1840
  if args.hook:
@@ -2229,6 +2268,12 @@ def cmd_migrate(root, args):
2229
2268
  # --------------------------------------------------------------------- main
2230
2269
 
2231
2270
  def main(argv=None):
2271
+ # `remind` is intercepted BEFORE argparse (kb's F-041 rule, kept): it is a
2272
+ # hook surface fired on every prompt, so a stray flag must cost a usage
2273
+ # message and a non-zero exit on a turn, not the turn itself.
2274
+ if (argv if argv is not None else sys.argv[1:])[:1] == ["remind"]:
2275
+ return cmd_remind(None)
2276
+
2232
2277
  common = argparse.ArgumentParser(add_help=False)
2233
2278
  common.add_argument("--root", help="project root (default: walk up until a docs root is found)")
2234
2279
  common.add_argument("--docs-dir", dest="docs_dir",
@@ -2273,6 +2318,10 @@ def main(argv=None):
2273
2318
  sub.add_parser("orient", parents=[common, hybrid_opt],
2274
2319
  help="SessionStart hook: emit docs-root orientation to stdout (fail-open, zero-execution)")
2275
2320
 
2321
+ sub.add_parser("remind", parents=[common],
2322
+ help="UserPromptSubmit hook: print the constant per-turn line "
2323
+ "(fail-open, zero-read, zero-execution)")
2324
+
2276
2325
  pp = sub.add_parser("plan", parents=[common],
2277
2326
  help="Subagent Execution: validate/brief a PLAN_[feature].md (zero-execution)")
2278
2327
  pp_sub = pp.add_subparsers(dest="plan_cmd", required=True)
@@ -2296,7 +2345,7 @@ def main(argv=None):
2296
2345
  # nothing is being guessed. Refusing here would make the guard block the
2297
2346
  # one command that ends the ambiguity.
2298
2347
  discovered, name = None, args.from_dir
2299
- elif args.cmd == "orient":
2348
+ elif args.cmd in ("orient", "remind"):
2300
2349
  # The SessionStart hook is fail-open by contract: it must never block a
2301
2350
  # session, not even on a half-migrated tree. It orients on the default
2302
2351
  # and says so rather than exiting non-zero.
@@ -2311,6 +2360,8 @@ def main(argv=None):
2311
2360
  return cmd_gate(args)
2312
2361
  if args.cmd == "orient":
2313
2362
  return cmd_orient(args)
2363
+ if args.cmd == "remind":
2364
+ return cmd_remind(args)
2314
2365
 
2315
2366
  root = Path(args.root).resolve() if args.root else (discovered or find_project_root())
2316
2367
  if args.cmd == "check":