@skyf0xx/hedgehog 4.0.13 → 4.0.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "4.0.13",
3
+ "version": "4.0.15",
4
4
  "description": "Install the Hedgehog build discipline (agents + skills) into a repo, for Claude Code, Cursor, or Gemini CLI.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -358,6 +358,61 @@ expressible: it requires a commit before the verify that gates the
358
358
  commit. Choose a model that applies directly rather than designing a
359
359
  layer around it.
360
360
 
361
+ ## Step 4d — make the product's own surface reachable
362
+
363
+ Skip this only if nothing the project publishes is reached through an
364
+ **arbitrator**: an ingress, a router, a gateway, a reverse proxy, a
365
+ process supervisor — anything that decides which backend answers a
366
+ request, and that no layer publishing a surface owns. For every core
367
+ with one, add a `once: true` **reachability layer** at the tail,
368
+ `depends_on` the last per-module layer, whose `verify` makes a real
369
+ request to each intent's primary surface and asserts something specific
370
+ about the answer.
371
+
372
+ Every other check in this skill judges one layer against its own claim.
373
+ This one exists because the defect it catches belongs to no layer. On a
374
+ module axis each layer's `scope` is a disjoint per-module subtree, so an
375
+ API layer choosing a route and a web layer choosing a screen path make
376
+ independent choices that only ever meet in the arbitrator's config —
377
+ owned by a third layer that knows neither. Each layer is individually
378
+ correct, each verify passes in isolation, and the request 404s. That is
379
+ a whole-graph property, and only a task that runs after the whole graph
380
+ can hold it.
381
+
382
+ What the layer asserts is the intent's `outcome` read back from outside:
383
+ the route answers, with a status and a body that could not come from the
384
+ wrong backend. Assert something the *other* side of the arbitrator
385
+ cannot produce — a JSON shape, a header, a field the API returns and the
386
+ web app does not. A bare 200 is satisfied by the fall-through app's own
387
+ 200, which is the exact confusion this layer exists to detect.
388
+
389
+ The tail position is doing real work here, and the same three properties
390
+ that make `once: true` right for a composition seam (Step 4) make it
391
+ right here:
392
+
393
+ - **One task, after everything.** `depends_on` the last per-module layer
394
+ makes it wait on *every* module's copy, so it runs once, with the
395
+ whole product standing up.
396
+ - **Re-entrant by construction.** When `planner`'s Re-entry pass adds a
397
+ module to a finished build, that module's task becomes a prerequisite
398
+ of this already-complete layer, so `hedgehog plan` reopens it — a new
399
+ module's surface gets checked for reachability without anyone
400
+ remembering to ask.
401
+ - **It closes the graph's own claim.** Without it, "every task complete"
402
+ is a statement about tasks. With it, the last task completing means
403
+ the product answers.
404
+
405
+ Two failure modes to design out. The layer must carry no `{module}`
406
+ anywhere — `validateCore` rejects a `once` layer that does — so it
407
+ enumerates the surfaces it checks in one command rather than
408
+ substituting a module in. And it needs the arbitrator's routing to
409
+ already be applied, which makes it strictly later than the deploy layer,
410
+ never merged into it: a deploy layer verifies its own manifests landed,
411
+ which is what six green deploy layers over a 404 already looked like.
412
+
413
+ Record in `core-design.md` which layer is the reachability gate and what
414
+ each surface it checks is asserting.
415
+
361
416
  ## Step 5 — write `.hedgehog/core.yaml`
362
417
 
363
418
  The loader parses `id` plus a `layers` list of flat maps. Every layer
@@ -487,6 +542,16 @@ if missed:
487
542
  deployment/app` read the same thing regardless of which module's task
488
543
  is running, so six modules' deploy tasks assert the identical claim six
489
544
  times rather than each module's own deployment.
545
+ - **A reachability layer's `verify` asserts the product answers, not
546
+ that a layer deployed.** The tail `once: true` layer Step 4d calls for
547
+ is the one place a `verify` command is about the whole build rather
548
+ than its own scope, so the usual scope/filter cross-checks above have
549
+ nothing to say about it. Its command makes a real request through the
550
+ arbitrator to each intent's primary surface and asserts a response
551
+ only the intended backend could produce. Its `scope` is whatever
552
+ fixture or script the check itself lives in — a layer that writes
553
+ nothing still needs a scope glob it may write, since `hedgehog verify`
554
+ rejects writes outside it.
490
555
  - **Declare the binaries `verify` needs, in `requires`.** Optional, an
491
556
  inline list alongside `scope`/`verify`/`commit`
492
557
  (`requires: ["terraform", "kubectl"]`), and only for tools that come
@@ -549,6 +614,9 @@ to change only until the file lands. Hard stop.
549
614
  (intents × layers tasks, or one task per layer).
550
615
  - For any layer that deploys or publishes: where the push happens
551
616
  relative to that layer's commit (Step 4c) — or that no layer deploys.
617
+ - Which layer is the reachability gate and what each surface it checks
618
+ asserts (Step 4d) — or that nothing published is reached through an
619
+ arbitrator.
552
620
  - That this is an authored core: the sequence was designed for this
553
621
  project, not battle-tested across many, and it carries the same
554
622
  enforcement as a Golden Core but a weaker guarantee.