@event4u/agent-config 8.7.0 → 8.9.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +109 -222
- package/CONTRIBUTING.md +25 -0
- package/README.md +7 -4
- package/dist/agent-src/commands/agent-handoff.md +39 -2
- package/dist/agent-src/commands/research/deep.md +8 -0
- package/dist/agent-src/commands/research/report.md +7 -0
- package/dist/agent-src/commands/roadmap/create.md +12 -0
- package/dist/agent-src/commands/roadmap/process-full.md +31 -5
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +30 -1
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +1 -1
- package/dist/agent-src/contexts/execution/orchestration-benchmark-gate.md +28 -16
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +36 -5
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +37 -0
- package/dist/agent-src/contexts/execution/subagent-topologies.md +34 -0
- package/dist/agent-src/contexts/execution/verification-mechanics.md +9 -9
- package/dist/agent-src/rules/active-remediation.md +92 -0
- package/dist/agent-src/rules/agent-authority.md +1 -1
- package/dist/agent-src/rules/broken-access-control.md +106 -0
- package/dist/agent-src/rules/communication-through-line.md +42 -0
- package/dist/agent-src/rules/decision-revisit-gate.md +8 -0
- package/dist/agent-src/rules/delegation-policy.md +5 -0
- package/dist/agent-src/rules/direct-answers.md +1 -1
- package/dist/agent-src/rules/downstream-changes.md +1 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +65 -0
- package/dist/agent-src/rules/git-history-discipline.md +18 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +6 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +66 -1
- package/dist/agent-src/rules/non-destructive-by-default.md +3 -0
- package/dist/agent-src/rules/prefer-enums-over-literals.md +65 -0
- package/dist/agent-src/rules/question-not-instruction.md +76 -0
- package/dist/agent-src/rules/senior-engineering-discipline.md +82 -0
- package/dist/agent-src/rules/think-before-action.md +1 -0
- package/dist/agent-src/rules/tool-safety.md +8 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +87 -22
- package/dist/agent-src/scripts/update_roadmap_progress.ts +2 -1
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +112 -0
- package/dist/agent-src/skills/ai-code-blindspots/evals/triggers.json +52 -0
- package/dist/agent-src/skills/authz-review/SKILL.md +21 -4
- package/dist/agent-src/skills/code-review/SKILL.md +2 -0
- package/dist/agent-src/skills/command-writing/SKILL.md +53 -40
- package/dist/agent-src/skills/dcf-modeling/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +1 -0
- package/dist/agent-src/skills/dependency-upgrade/SKILL.md +20 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +49 -12
- package/dist/agent-src/skills/design-review/SKILL.md +29 -15
- package/dist/agent-src/skills/design-system-capture/SKILL.md +63 -24
- package/dist/agent-src/skills/forecasting/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +104 -0
- package/dist/agent-src/skills/frontend-render-security/evals/triggers.json +52 -0
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -0
- package/dist/agent-src/skills/mcp/SKILL.md +24 -0
- package/dist/agent-src/skills/mcp-builder/SKILL.md +9 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +55 -4
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +46 -5
- package/dist/agent-src/skills/runway-cognition/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/scenario-modeling/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +7 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +12 -3
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +27 -33
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +112 -0
- package/dist/agent-src/skills/supply-chain-intake/evals/triggers.json +52 -0
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +72 -2
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -0
- package/dist/agent-src/skills/unit-economics-modeling/evals/domain-truth.json +40 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +60 -0
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +268 -46
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +18 -8
- package/dist/discovery/trust-report.md +11 -11
- package/dist/discovery/workspaces.json +16 -6
- package/dist/install/install.mjs +141 -3
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
- package/dist/mcp/registry-manifest.json +4 -4
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/claude_builtin_names.js +145 -0
- package/dist/scripts/_lib/claude_builtin_names.js.map +1 -0
- package/dist/scripts/condense.js +14 -0
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +2 -1
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/CLAIMS.md +58 -2
- package/docs/architecture.md +3 -3
- package/docs/archive/CHANGELOG-pre-8.9.0.md +306 -0
- package/docs/benchmark.md +113 -0
- package/docs/catalog.md +32 -5
- package/docs/contracts/command-clusters.md +18 -0
- package/docs/contracts/design-artifact-verification.md +124 -0
- package/docs/contracts/injected-block-authenticity-model.md +66 -0
- package/docs/contracts/mcp-tool-inventory.md +31 -30
- package/docs/decisions/ADR-061-corpus-grounding-layer.md +13 -0
- package/docs/decisions/ADR-105-automatic-subagent-orchestration.md +6 -0
- package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +11 -2
- package/docs/decisions/ADR-117-subagents-auto-default-on.md +88 -0
- package/docs/decisions/INDEX.md +1 -0
- package/docs/experiences/content_creator.md +2 -0
- package/docs/experiences/finance.md +2 -0
- package/docs/experiences/founder.md +2 -0
- package/docs/experiences/ops.md +2 -0
- package/docs/featured-skills.md +2 -2
- package/docs/getting-started-by-role.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/governance-advantage.md +1 -1
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +27 -0
- package/docs/guidelines/agent-infra/emphasis-budget.md +45 -0
- package/docs/guidelines/agent-infra/memory-access.md +12 -0
- package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +151 -0
- package/docs/guidelines/agent-infra/tool-description-as-policy.md +35 -0
- package/docs/guidelines/design-canon.md +1 -0
- package/docs/proof.md +60 -3
- package/docs/release-runbook.md +138 -0
- package/docs/second-brain-scope.md +116 -0
- package/docs/succession.md +80 -0
- package/package.json +2 -2
- package/src/agent-src/scripts/update_roadmap_progress.ts +2 -1
- package/src/config/agent-settings.template.yml +25 -6
- package/src/scripts/_dispatch.bash +27 -0
- package/src/scripts/_lib/claude_builtin_names.ts +148 -0
- package/src/scripts/_lib/file_slicer.ts +117 -0
- package/src/scripts/_lib/lexical_index.ts +155 -0
- package/src/scripts/_lib/orchestration_gate.ts +12 -8
- package/src/scripts/_lib/orchestration_record.ts +170 -0
- package/src/scripts/_lib/orchestration_savings.ts +180 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +71 -0
- package/src/scripts/_lib/security_lint.ts +1 -0
- package/src/scripts/_lib/stat_index.ts +98 -0
- package/src/scripts/benchmark.ts +122 -0
- package/src/scripts/build_proof.ts +51 -0
- package/src/scripts/chat_history.ts +79 -7
- package/src/scripts/check_memory.ts +2 -0
- package/src/scripts/check_quality_regression.ts +51 -0
- package/src/scripts/check_roadmap_trackable.ts +3 -2
- package/src/scripts/condense.ts +14 -0
- package/src/scripts/discovery_graph.ts +0 -0
- package/src/scripts/domain_soundness_status.ts +236 -0
- package/src/scripts/external_sources_denylist.json +4 -1
- package/src/scripts/generate_index.ts +17 -0
- package/src/scripts/hot_context_hook.ts +47 -2
- package/src/scripts/install-hooks.sh +25 -0
- package/src/scripts/install.ts +35 -2
- package/src/scripts/learning_sidecar.ts +0 -0
- package/src/scripts/lint_agent_skill_names.ts +29 -0
- package/src/scripts/lint_behavioural_eval_freshness.ts +140 -0
- package/src/scripts/lint_knowledge_scale.ts +4 -3
- package/src/scripts/lint_roadmap_complexity.ts +124 -0
- package/src/scripts/lint_versioned_cache.ts +195 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +23 -3
- package/src/scripts/mcp_server/tools.ts +182 -1
- package/src/scripts/measure_lexical_ranking.ts +173 -0
- package/src/scripts/memory_lookup.ts +0 -0
- package/src/scripts/memory_replay.ts +285 -0
- package/src/scripts/orchestration_record.ts +140 -0
- package/src/scripts/orchestration_savings_report.ts +124 -0
- package/src/scripts/schemas/domain-truth.schema.json +94 -0
- package/src/scripts/schemas/evals.schema.json +113 -0
- package/src/scripts/schemas/skill.schema.json +4 -0
- package/src/scripts/score_domain_truth.ts +192 -0
- package/src/scripts/second_brain_retrieval.ts +278 -0
- package/src/scripts/second_brain_run.ts +341 -0
- package/src/scripts/second_brain_score.ts +186 -0
- package/src/scripts/session_memory_index.ts +94 -0
- package/src/scripts/skill_eval_coverage.ts +318 -0
- package/src/server/schemas/settings.ts +5 -2
|
@@ -232,6 +232,18 @@ Write the pick to frontmatter as `execution.mode:`
|
|
|
232
232
|
and expensive as a mid-run ambiguity halt; pre-existing `[~]` items
|
|
233
233
|
guarantee the archival gate interrupts the run.
|
|
234
234
|
`lint_roadmap_complexity` warns on the same patterns as a backstop.
|
|
235
|
+
- **Human-gate scan (ALL picks):** scan drafted steps for human-gate
|
|
236
|
+
patterns (`user verifies/reviews/approves`, `manually check`,
|
|
237
|
+
`sign-off`, `wait for approval`, `ask the user`, "Review / Sign-off"
|
|
238
|
+
phases). Human checkpoints default to **zero** (template rule 22) —
|
|
239
|
+
rewrite each hit as agent-verifiable check (command, targeted test)
|
|
240
|
+
**before saving**, or promote to structured `## Blockers` entry:
|
|
241
|
+
**human gate** only when only a human can decide/authorize
|
|
242
|
+
(Hard-Floor authorization, billable spend, contested decision);
|
|
243
|
+
**external dependency** becomes a blocker whose `Resolved when:`
|
|
244
|
+
names an agent-checkable status probe — never a human gate. Do not
|
|
245
|
+
author safety floors as steps — they fire at run time on their own.
|
|
246
|
+
`lint_roadmap_complexity` warns on the same patterns as a backstop.
|
|
235
247
|
|
|
236
248
|
### 11. Hand back — HARD STOP, never auto-offer execution
|
|
237
249
|
|
|
@@ -57,7 +57,11 @@ with the **scope delta below**.
|
|
|
57
57
|
(`false` / missing) the pipeline never runs locally; remote CI is
|
|
58
58
|
the gate. On red → stop, surface, do **not** silently roll into
|
|
59
59
|
the next phase. Under `mode: phase-checkpoints`, additionally emit
|
|
60
|
-
|
|
60
|
+
a compact status LINE at every boundary — but under `process-full`
|
|
61
|
+
this is a NON-BLOCKING status, NOT a stop-and-wait: the run continues
|
|
62
|
+
to the next phase immediately. `phase-checkpoints` narrows to a
|
|
63
|
+
stop-and-wait only under `/roadmap:process-phase`, never here (a
|
|
64
|
+
process-full invocation overrides the mode's boundary-wait). Under
|
|
61
65
|
`autonomous`, boundaries are silent (quality pipeline aside).
|
|
62
66
|
- **Final archival:** when the roadmap is fully closed, run the
|
|
63
67
|
archival check from
|
|
@@ -66,10 +70,32 @@ with the **scope delta below**.
|
|
|
66
70
|
## Iron Law — Full is Full
|
|
67
71
|
|
|
68
72
|
```
|
|
69
|
-
/roadmap:process-full PROCESSES EVERY OPEN STEP IN THE FILE
|
|
70
|
-
PHASE
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
/roadmap:process-full IS LAW: IT PROCESSES EVERY OPEN STEP IN THE FILE,
|
|
74
|
+
TO COMPLETION, ACROSS EVERY PHASE. ONLY THE FIVE HALT CONDITIONS STOP IT.
|
|
75
|
+
PHASE-INTERNAL "(DEFERRED)" / "(OPTIONAL)" / "GATED ON PHASE X" NOTES DO
|
|
76
|
+
NOT NARROW THE WORKING SET. A PHASE BOUNDARY IS NOT A STOP.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The **five — and only five — halt conditions** (exhaustive; nothing else
|
|
80
|
+
stops the run):
|
|
81
|
+
|
|
82
|
+
1. **Hard-Floor** trigger ([`non-destructive-by-default`](../../rules/non-destructive-by-default.md)).
|
|
83
|
+
2. **Council-off + genuine ambiguity** (only outside an accepted contract with council available).
|
|
84
|
+
3. **Security-sensitive** surface reached.
|
|
85
|
+
4. **Scope-out-of-roadmap** work discovered.
|
|
86
|
+
5. **Test / quality red** that cannot be cleared within the N=3 budget.
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
FORBIDDEN NON-HALT REASONS — NEVER STOP THE RUN FOR ANY OF THESE:
|
|
90
|
+
· "running low on context / token budget"
|
|
91
|
+
· "quality would degrade / deserves a fresh focused run later"
|
|
92
|
+
· "avoid a PR pile-up" / "let the open PRs merge first"
|
|
93
|
+
· "this phase is large / touches a deep subsystem"
|
|
94
|
+
· "phase-checkpoints mode, so I'll checkpoint and wait"
|
|
95
|
+
· any agent-invented caution not in the five halt conditions above.
|
|
96
|
+
INVENTING A HALT REASON IS A VIOLATION OF THE COMMAND AND THE USER'S WILL.
|
|
97
|
+
IF CONTEXT RUNS OUT MID-RUN, KEEP LANDING COMPLETE STEPS UNTIL IT DOES —
|
|
98
|
+
NEVER ANNOUNCE A BOUNDARY-STOP BY CHOICE.
|
|
73
99
|
```
|
|
74
100
|
|
|
75
101
|
Phase-internal `(deferred)` / `(optional)` / `gated on Phase N` tags are
|
|
@@ -43,7 +43,7 @@ behavior. Cannot → read more.
|
|
|
43
43
|
## Consult memory before editing
|
|
44
44
|
|
|
45
45
|
Invariants live in the memory layer; architectural rationale lives in ADRs.
|
|
46
|
-
Via [`memory-access`](
|
|
46
|
+
Via [`memory-access`](../../../../../../docs/guidelines/agent-infra/memory-access.md),
|
|
47
47
|
call `retrieve(types=["domain-invariants"], keys=<touched paths>, limit=3)` —
|
|
48
48
|
a matching `domain-invariant` is a hard constraint. For *why* the current
|
|
49
49
|
shape exists, check the ADR index `docs/decisions/INDEX.md`. Cite the `id` /
|
|
@@ -96,3 +96,32 @@ context only — does NOT mean the user's message is about that file.
|
|
|
96
96
|
- Treat the open file as relevant only when the user's message
|
|
97
97
|
explicitly references it (e.g. "fix this file", "what does this
|
|
98
98
|
do?", "update the open file").
|
|
99
|
+
|
|
100
|
+
## Goal-driven execution — vague ask → verifiable goal
|
|
101
|
+
|
|
102
|
+
Before executing a multi-step task, transform it into criteria a tool can
|
|
103
|
+
check. Strong criteria let the loop run independently; weak criteria ("make it
|
|
104
|
+
work") guarantee rework.
|
|
105
|
+
|
|
106
|
+
| Vague ask | Verifiable goal |
|
|
107
|
+
|---|---|
|
|
108
|
+
| "Add validation" | Write tests for the invalid inputs, then make them pass |
|
|
109
|
+
| "Fix the bug" | Write a test that reproduces it (fails), then make it pass |
|
|
110
|
+
| "Refactor X" | Tests green before AND after; diff shows no behavior change |
|
|
111
|
+
| "Make it faster" | Name the metric + target (p95 < 200ms); measure before/after |
|
|
112
|
+
| "Clean this up" | Clarification trigger — no checkable success state exists yet |
|
|
113
|
+
|
|
114
|
+
For multi-step tasks, state the plan with a verify per step:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
1. [step] → verify: [check]
|
|
118
|
+
2. [step] → verify: [check]
|
|
119
|
+
3. [step] → verify: [check]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A step without a `verify:` is not a step — it is an assumption. A task whose
|
|
123
|
+
success cannot be checked is a clarification trigger, not an execution
|
|
124
|
+
trigger (route to `ask-when-uncertain`).
|
|
125
|
+
|
|
126
|
+
Wrong/right demos:
|
|
127
|
+
[`simplicity-and-goal-demos`](../../../../../../docs/guidelines/agent-infra/simplicity-and-goal-demos.md).
|
|
@@ -12,7 +12,7 @@ whether dispatch is physically possible on the host).
|
|
|
12
12
|
| Key | Default | Purpose |
|
|
13
13
|
|---|---|---|
|
|
14
14
|
| `subagents.enabled` | `true` | Global master switch. `false` = the whole layer is off — the canonical kill-switch. No auto-dispatch, no routing, everything runs in-session. |
|
|
15
|
-
| `subagents.auto` | `
|
|
15
|
+
| `subagents.auto` | `on` | Auto-dispatch mode: `off` \| `ask` \| `on`. Default `on` on subagent-capable hosts (flipped from `ask` 2026-07-09, ADR-117), `off` elsewhere. |
|
|
16
16
|
| `subagents.downshift` | `true` | Route delegable sub-tasks to the lowest-capable tier (Phase 2). |
|
|
17
17
|
| `subagents.quota_arbitrage` | `true` | Prefer a separate-quota-pool model where the host manifest allows it (Phase 2 bonus). |
|
|
18
18
|
| `subagents.model_map` | `{}` | Optional per-tier model override for downshift routing. |
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
# Orchestration Benchmark &
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
# Orchestration Benchmark & Demotion Gate (Phase 6 → telemetry-demotion)
|
|
2
|
+
|
|
3
|
+
Originally the falsification gate the auto-orchestration roadmap was built
|
|
4
|
+
around: shipped default for `subagents.auto` would flip toward `on` only on a
|
|
5
|
+
benchmark proving a net win at held quality.
|
|
6
|
+
|
|
7
|
+
**As of 2026-07-09 the shipped default IS `on`** on subagent-capable hosts
|
|
8
|
+
(ADR-117; [`orchestration-default-flip-verdict`](../../../../agents/settings/contexts/orchestration-default-flip-verdict.md)
|
|
9
|
+
§ 2026-07-09). Flipped on a **bounded-downside re-evaluation**, not a passed
|
|
10
|
+
benchmark: rigorous paired bench:ab not producible here (no runtime executing
|
|
11
|
+
model `Task`/`Agent` calls), win-validation deadlocked (telemetry needs `on`,
|
|
12
|
+
`on` gated on telemetry). Re-eval broke the deadlock — downside of `on` is
|
|
13
|
+
structurally small (delegable slices cost-routed to cheapest tier,
|
|
14
|
+
classification fires only on real structural signals, every return verified) →
|
|
15
|
+
`on` gathers realized telemetry responsibly. This gate now governs
|
|
16
|
+
**demotion**: measured regression flips default back to `ask`.
|
|
7
17
|
|
|
8
18
|
## Measurement — pin the method
|
|
9
19
|
|
|
@@ -30,18 +40,20 @@ HONEST-NULL EXIT: no win, or quality regressed → KEEP the conservative default
|
|
|
30
40
|
|
|
31
41
|
`gateVerdict()` encodes pass = `net_win && quality_held`.
|
|
32
42
|
|
|
33
|
-
## Default
|
|
43
|
+
## Default state + demotion — host-gated
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
Shipped default for `subagents.auto` is `on` on hosts whose manifest reports
|
|
46
|
+
`subagent_spawn: true` (`off` elsewhere) — set directly in
|
|
47
|
+
`src/config/agent-settings.template.yml`. `resolveShippedDefault()` remains the
|
|
48
|
+
host-gated resolver, now serving **demotion**: fed a `fail` verdict from
|
|
49
|
+
accumulated real-world orchestration telemetry, resolves back to `ask`
|
|
50
|
+
(conservative). A `pass` keeps `on`. Demotion path is a one-line edit,
|
|
51
|
+
host-gated by the manifest at runtime.
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
The 2026-07-09 flip reconciles the user's "default on" goal with the council's
|
|
54
|
+
"prove-first": rather than wait on a benchmark that cannot be produced here,
|
|
55
|
+
`on` ships because its downside is bounded and reversible, and realized
|
|
56
|
+
telemetry is now free to accrue and drive demotion if it regresses.
|
|
45
57
|
|
|
46
58
|
## Reproducibility
|
|
47
59
|
|
|
@@ -23,6 +23,8 @@ v1 forward-compat rule on unknown fields).
|
|
|
23
23
|
"task_class": null,
|
|
24
24
|
"tier_chosen": null,
|
|
25
25
|
"tier_source": null,
|
|
26
|
+
"dispatch_tokens": null,
|
|
27
|
+
"session_tier": null,
|
|
26
28
|
"escalated_from": null,
|
|
27
29
|
"verify_result_by_tier": null
|
|
28
30
|
}
|
|
@@ -46,8 +48,10 @@ v1 forward-compat rule on unknown fields).
|
|
|
46
48
|
| `tier_source` | enum \| null | Where `tier_chosen` came from: `static` (frontmatter/category pin) · `inferred` (deterministic per-slice inference) · `inherit` (session tier — no downshift decision). Lets the evidence gate score inferred routing separately from static pinning. |
|
|
47
49
|
| `escalated_from` | string \| null | When the slice re-dispatched after a verification failure: tier of the FAILED attempt (e.g. `lite` when a lite return failed verify and the slice re-ran on `medium`). `null` = no escalation. |
|
|
48
50
|
| `verify_result_by_tier` | object \| null | Map tier → verification result per attempt of this slice (e.g. `{"lite":"fail","medium":"pass"}`). Values: `pass` \| `fail` \| `skipped`. Feeds the per-tier verify-pass-rate tripwire. Enums only, no verdict bodies. |
|
|
51
|
+
| `dispatch_tokens` | int \| null | **Cost-% extension.** Absolute tokens the dispatched slice consumed (measured subagent usage). Feeds the MODELED cost-% in `orchestration_savings_report`, which needs an absolute base the token-count delta lacks. `null` = not recorded. Counts only. |
|
|
52
|
+
| `session_tier` | string \| null | **Cost-% extension.** The orchestrator's OWN tier — the baseline the downshift cost-% measures `tier_chosen` against (a `high`→`lite` downshift is a rate win the token count can't see). `null` = not recorded. |
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
These routing + cost fields are additive and optional — a line without them is
|
|
51
55
|
still a valid orchestration line; readers ignore unknown fields per the v1
|
|
52
56
|
forward-compat rule.
|
|
53
57
|
|
|
@@ -74,10 +78,22 @@ audit-log-v1 path; `YYYY-MM` = current UTC month). The line is a standard
|
|
|
74
78
|
audit-log-v1 object with `input_kind: "orchestration"` and an `orchestration`
|
|
75
79
|
sub-object carrying this shape.
|
|
76
80
|
|
|
77
|
-
**No hook or daemon required.** The agent writes
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
**No hook or daemon required.** The agent writes the line directly —
|
|
82
|
+
no-runtime-compatible capture path (decided 2026-06-30, maintainer decision; see
|
|
83
|
+
`road-to-subagent-value-realization.md § Council notes`). Do NOT hand-author the
|
|
84
|
+
JSON: run the recorder, which validates + builds a conformant line from the
|
|
85
|
+
counts you already have:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
./scripts-run src/scripts/orchestration_record --spawn-count <n> \
|
|
89
|
+
--token-delta <±n> --provenance measured|estimated \
|
|
90
|
+
[--tier-chosen lite|medium|high] [--tier-source static|inferred|inherit] \
|
|
91
|
+
[--task-class <id>] [--tiers a,b] [--dispatch-outcome DONE|BLOCKED|…]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
A capture **hook** is the wrong tool: the PostToolUse payload carries no subagent
|
|
95
|
+
token usage (only the orchestrator sees it) and a hook reverses the 2026-06-30
|
|
96
|
+
no-hook decision. Reader: `src/scripts/orchestration_savings_report.ts`.
|
|
81
97
|
|
|
82
98
|
**`token_delta` sourcing priority:**
|
|
83
99
|
1. Host-reported: read `usage.output_tokens` (and `usage.input_tokens` if
|
|
@@ -87,6 +103,21 @@ capture path (decided 2026-06-30, maintainer decision; see
|
|
|
87
103
|
response length (chars / 4 ≈ tokens). Set `token_delta_provenance:
|
|
88
104
|
"estimated"`. Lossy; measured always preferred.
|
|
89
105
|
|
|
106
|
+
## Savings report
|
|
107
|
+
|
|
108
|
+
Aggregate accumulated telemetry into a token-savings report:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
./scripts-run src/scripts/orchestration_savings_report [--dir <path>] [--format text|json]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Sums `token_delta` across dispatches (negative = net saved); splits by
|
|
115
|
+
provenance (measured vs estimated), `tier_chosen`, `task_class`. Reports
|
|
116
|
+
**ABSOLUTE net tokens saved, never a percentage**: telemetry records net delta,
|
|
117
|
+
not the absolute in-session baseline → "% of session saved" not derivable. A
|
|
118
|
+
percentage needs an additive absolute-baseline field (deferred follow-up).
|
|
119
|
+
Reader: [`src/scripts/_lib/orchestration_savings.ts`](../../../../src/scripts/_lib/orchestration_savings.ts).
|
|
120
|
+
|
|
90
121
|
## Related
|
|
91
122
|
|
|
92
123
|
- [`audit-log-v1`](../../../../docs/contracts/audit-log-v1.md) — the frozen
|
|
@@ -28,6 +28,25 @@ roadmaps with open work that is blocked-for-later — parked, not active).
|
|
|
28
28
|
surface alternatives in the pre-run summary.
|
|
29
29
|
- None active → tell the user; suggest [`/roadmap:create`](../../commands/roadmap/create.md).
|
|
30
30
|
|
|
31
|
+
### Live merge-state before any "in-flight / merged / handled" claim
|
|
32
|
+
|
|
33
|
+
Selection reasoning that **excludes** a roadmap — or a pre-run summary,
|
|
34
|
+
decision, or user-facing message that **describes** one — as *in-flight*,
|
|
35
|
+
*handled by an open PR*, *already merged*, or *not yet merged* → verify **live
|
|
36
|
+
at claim time** (`gh pr view <n> --json state,mergedAt` / `gh pr list --search
|
|
37
|
+
"<slug>"`). Never infer merge state from:
|
|
38
|
+
|
|
39
|
+
- **dashboard open-count** — lags: a completed roadmap archives in the
|
|
40
|
+
**merging** PR, so it can read "open" on `main` while its PR is already
|
|
41
|
+
merged, or read "open" seconds before a merge you never re-checked;
|
|
42
|
+
- **session memory** ("opened a PR for this earlier") or an **earlier fetch** —
|
|
43
|
+
a merge can land between fetch and message.
|
|
44
|
+
|
|
45
|
+
Stale merge-state claim ships an unnecessary message about work already done —
|
|
46
|
+
the failure this prevents. The [`direct-answers`](../../rules/direct-answers.md)
|
|
47
|
+
Iron-Law-2 live-state rule (git/PR state never from memory), applied to
|
|
48
|
+
roadmap selection.
|
|
49
|
+
|
|
31
50
|
## 2. Pre-run summary — gate or inline note
|
|
32
51
|
|
|
33
52
|
Read `roadmap.skip_pre_run_gate` from `.agent-settings.yml` (default
|
|
@@ -259,6 +278,24 @@ drafting, council enablement), not safety.
|
|
|
259
278
|
|
|
260
279
|
On halt: stop, surface state, do **not** auto-fix outside the failing step.
|
|
261
280
|
|
|
281
|
+
### Forbidden non-halt reasons — agent-invented cautions
|
|
282
|
+
|
|
283
|
+
Halt list above is exhaustive. Never stop a `process-full` (or any wrapper) run
|
|
284
|
+
for an invented reason not on it. NOT halt conditions — stopping for them
|
|
285
|
+
violates the command + user's will:
|
|
286
|
+
|
|
287
|
+
- "low context / token budget" — keep landing complete steps until context
|
|
288
|
+
actually runs out; never announce a boundary-stop by choice.
|
|
289
|
+
- "quality would degrade / deserves a fresh focused run later"
|
|
290
|
+
- "avoid a PR pile-up" / "let open PRs merge first"
|
|
291
|
+
- "this phase is large / deep subsystem"
|
|
292
|
+
- "phase-checkpoints, so I'll checkpoint and wait" — under `process-full` a
|
|
293
|
+
phase boundary emits a non-blocking status line and the run continues; the
|
|
294
|
+
stop-and-wait reading applies only to `process-phase`.
|
|
295
|
+
|
|
296
|
+
Genuine inability to continue = one of the five real halt conditions fired —
|
|
297
|
+
surface THAT, not a manufactured caution.
|
|
298
|
+
|
|
262
299
|
### Non-halt — gating notes, "optional" tags
|
|
263
300
|
|
|
264
301
|
The following are **authoring annotations**, never halt conditions. Do
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Subagent Topologies — per-mode communication shape
|
|
2
|
+
|
|
3
|
+
Descriptive, not enforced. Documents the **expected agent-to-agent
|
|
4
|
+
communication topology** for each [`subagent-orchestration`](../../skills/subagent-orchestration/SKILL.md)
|
|
5
|
+
mode so consumers can predict latency, failure modes, and where consensus
|
|
6
|
+
is required. Cited from
|
|
7
|
+
an internal findings note (local-only) <!-- council-ref-allowed: ADR decision trace for topology anti-drift defaults -->
|
|
8
|
+
row 7 (the external runtime's `hierarchical, 6–8 agents, raft consensus` anti-drift
|
|
9
|
+
default).
|
|
10
|
+
|
|
11
|
+
| Mode | Topology | Anti-drift default | Notes |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| do-and-judge | `hierarchical` | 1 implementer · 1 judge · session-orchestrated | Two-node hub-and-spoke; orchestrator owns the loop. |
|
|
14
|
+
| do-and-judge-two-stage | `hierarchical` | 1 implementer · 2 sequential judges | Stages are serialized; spec-judge gates quality-judge. |
|
|
15
|
+
| do-in-steps | `ring` | N steps · 1 judge between each | Step N output → judge → step N+1 input; cycle on revise. |
|
|
16
|
+
| do-in-parallel | `star` | 6–8 implementers · 1 judge · session-hub | Capped by `subagents.max_parallel`; judge runs once on union. |
|
|
17
|
+
| do-competitively | `mesh` | 2–4 implementers · 1 judge | Implementers do not see each other; judge sees all candidates. |
|
|
18
|
+
| judge-with-debate | `hierarchical-mesh` | 2 judges · 1 meta-judge | Judges debate (mesh edge); meta-judge reconciles (hierarchical). |
|
|
19
|
+
| do-in-worktrees | `adaptive` | per-step topology of the underlying mode | Each worktree picks its own shape; chain is hierarchical. |
|
|
20
|
+
|
|
21
|
+
**Anti-drift default** (the external runtime convention, descriptive only):
|
|
22
|
+
`hierarchical, 6–8 agents, raft consensus`. Consumers free to
|
|
23
|
+
override per orchestration — the table is the **starting point**,
|
|
24
|
+
not a constraint. Topology is metadata for capacity planning, not
|
|
25
|
+
runtime-enforced.
|
|
26
|
+
|
|
27
|
+
**Glossary:**
|
|
28
|
+
|
|
29
|
+
- `hierarchical` — orchestrator hub; agents reply to hub only.
|
|
30
|
+
- `mesh` — agents see each other's outputs (e.g. competing diffs).
|
|
31
|
+
- `hierarchical-mesh` — peer debate followed by hub reconciliation.
|
|
32
|
+
- `ring` — output of step N feeds input of step N+1 in order.
|
|
33
|
+
- `star` — N agents fan out from a single hub; no peer comms.
|
|
34
|
+
- `adaptive` — topology shifts per step; outer chain remains hub.
|
|
@@ -11,15 +11,15 @@ incidents.
|
|
|
11
11
|
|
|
12
12
|
## Gate zero — `quality.local_auto_run` (default `false`)
|
|
13
13
|
|
|
14
|
-
`false
|
|
15
|
-
proactively — never ask
|
|
16
|
-
|
|
17
|
-
(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
`false`/missing → **never run the full quality pipeline or full test
|
|
15
|
+
suites proactively — never ask**; remote CI is the gate. Run one ONLY on:
|
|
16
|
+
(1) explicit ask this turn, (2) concrete CI failure — run exactly that
|
|
17
|
+
check, (3) new-gate carve-out (a NEW gate/test this change adds, run once).
|
|
18
|
+
Completion wording: *"quality gates delegated to remote CI"* — never a pass
|
|
19
|
+
claim for tools that did not run. Narrow probes stay allowed (one `--filter`
|
|
20
|
+
test, `curl`); a type-check + lint **scoped to the changed files** is
|
|
21
|
+
REQUIRED before a source push/PR — NOT the full pipeline. Everything below
|
|
22
|
+
applies when `true`.
|
|
23
23
|
|
|
24
24
|
## When to run what — timing matters (`local_auto_run: true`)
|
|
25
25
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: "auto"
|
|
3
|
+
tier: "2b"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
description: "Spotted an issue while working (security gap, missing test, bad code, duplication, stale idiom) — never ignore; fix small+aligned inline, ask on bigger, propose a follow-up PR for many"
|
|
6
|
+
triggers:
|
|
7
|
+
- intent: "spotted an unrelated issue while working"
|
|
8
|
+
- intent: "found legacy or duplicated code"
|
|
9
|
+
- keyword: "refactor"
|
|
10
|
+
- keyword: "duplicate"
|
|
11
|
+
- keyword: "legacy"
|
|
12
|
+
- keyword: "deprecated"
|
|
13
|
+
- keyword: "modernize"
|
|
14
|
+
- keyword: "upgrade"
|
|
15
|
+
- keyword: "cleanup"
|
|
16
|
+
- keyword: "technical debt"
|
|
17
|
+
workspaces: [engineering]
|
|
18
|
+
packs: [engineering-base]
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Active Remediation
|
|
22
|
+
|
|
23
|
+
The user doesn't see the code at the spot you're editing — you do. So this package guides the agent to **actively improve** what it touches: don't accept legacy, debt, security gaps, or duplication as given. But active improvement is bounded — never the scope creep `minimal-safe-diff` exists to stop. The rule is a **ladder**: act by size and alignment, and **never silently ignore** a real issue.
|
|
24
|
+
|
|
25
|
+
## The Iron Law
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
NEVER IGNORE A REAL ISSUE YOU SPOT — AT MINIMUM, NOTE IT.
|
|
29
|
+
SMALL + TASK-ALIGNED → FIX IT NOW, IN THE SAME CHANGE, WITH ITS TEST.
|
|
30
|
+
BIGGER OR DIVERGENT → NOTE IT, FINISH THE TASK, THEN ASK — NEVER MID-FLOW.
|
|
31
|
+
TOO MANY → PROPOSE ONE OR MORE SEPARATE FOLLOW-UP PRs THE USER REVIEWS.
|
|
32
|
+
A LIVE SECURITY / DATA-EXPOSURE GAP IS NEVER MERELY "NOTED FOR LATER".
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## What to remediate
|
|
36
|
+
|
|
37
|
+
Security gaps (broken access control, injection, secrets, missing authz — see `broken-access-control`) · missing/weak test coverage · bad code (dead code, silent error-swallow, unguarded edge cases) · **duplication** (same/near-same logic repeated instead of one method/abstraction) · **stale version-idioms** (code for an older language/framework version than the project runs).
|
|
38
|
+
|
|
39
|
+
## The ladder — classify, then act
|
|
40
|
+
|
|
41
|
+
### Fix now (autonomous, inline) — a bounded amendment to `minimal-safe-diff`
|
|
42
|
+
|
|
43
|
+
Allowed **only** when ALL hold — the testable "small + task-aligned" definition:
|
|
44
|
+
- **Same request path / module** as the current task (not an unrelated feature).
|
|
45
|
+
- **≤ ~10 changed lines** in one production file plus its test file — no wider cross-file ripple.
|
|
46
|
+
- **No public-API / response-shape change**, no new parameter.
|
|
47
|
+
- **No dependency bump, no migration, no data change.**
|
|
48
|
+
- **Its verification ships in the same commit** — the security fix's negative test, the correctness fix's case.
|
|
49
|
+
|
|
50
|
+
Under those constraints the fix is auditable in the same diff and is *not* scope creep — it corrects a boundary the agent touched. Anything outside them → **note + ask**, never auto. (`minimal-safe-diff` still governs the rest: no reformatting, no opportunistic refactor, no drive-by rename.)
|
|
51
|
+
|
|
52
|
+
### Note + ask (batched)
|
|
53
|
+
|
|
54
|
+
Bigger, or diverges from the task → do **not** refactor inline, do **not** interrupt the flow. Note the site (file:line + issue). Surface the batch as **one** numbered-options prompt (`user-interaction`, one recommendation line) **after the task is delivered** — or mid-work only when the flow makes it natural. Each option carries a real trade-off (`no-cheap-questions`). Refactor only on an explicit yes, as a separate scoped change (`scope-control`, `downstream-changes`).
|
|
55
|
+
|
|
56
|
+
### Propose a follow-up PR (many spots)
|
|
57
|
+
|
|
58
|
+
Too many to fold in without blowing the current diff's scope → propose **one or more separate follow-up PRs** (or a roadmap under `agents/roadmaps/`, using the shared-prefix convention) so the user reviews before merge. Creating the PR/roadmap is **permission-gated** — propose, get a yes, then create (`scope-control`, `commit-policy`).
|
|
59
|
+
|
|
60
|
+
## Version-gated modernization
|
|
61
|
+
|
|
62
|
+
Update stale idioms to the version the project **actually runs** — only when that version is **verified**:
|
|
63
|
+
- **Establish the version first** from the manifest constraint (`composer.json` `require`, `package.json` `engines`, `.tool-versions`, lockfile) — use the **lowest** bound; on an ambiguous/monorepo range, ask once. **Unknown version → do not touch** (must fit the project structure — the `source-discovery` gate).
|
|
64
|
+
- **Syntax-only, behavior-preserving idioms** (e.g. `array()` → `[]`, `isset($x)?$x:$d` → `$x ?? $d`, concat → template) provably equivalent → treat as a small fix (auto per the ladder).
|
|
65
|
+
- **Behavioral changes** (`readonly`/typed properties, new semantics) and **any dependency/version bump** → **ask only**, never auto (a version bump stays under `minimal-safe-diff`'s no-dependency-bump prohibition; a pure syntax idiom is categorically different).
|
|
66
|
+
|
|
67
|
+
## Live-security carve-out (priority)
|
|
68
|
+
|
|
69
|
+
A **live cross-user / cross-tenant data exposure** (`broken-access-control`) is not "note for later": a potential GDPR-notifiable breach (Art. 33, 72 h from discovery). Meets the fix-now bar → fix now with its negative test. Otherwise → **stop and surface it immediately** (a safety surface, visible even under an autonomous mandate — like the `no-pr-progress-comments` safety carve-out). Never defer silently, never look away.
|
|
70
|
+
|
|
71
|
+
## Guardrails — don't become a nagging machine
|
|
72
|
+
|
|
73
|
+
- Subordinate to `no-cheap-questions` (self-check items 3 & 14 — real trade-off, not a disguised continuation/commit ask), `autonomous-execution` (the end-of-session batch must not read as "shall I continue?"), `user-interaction`, `ask-when-uncertain` (one batched prompt = one question).
|
|
74
|
+
- Threshold to surface at all: a **real, nameable** improvement with concrete benefit. Cosmetic nitpicks with no trade-off → drop silently. The live-security carve-out is the only case that interrupts or overrides autonomy.
|
|
75
|
+
|
|
76
|
+
## When it fires
|
|
77
|
+
|
|
78
|
+
While implementing/modifying code you pass an issue outside the literal task. Also on explicit "clean this up / modernize / dedupe".
|
|
79
|
+
|
|
80
|
+
## When NOT to fire
|
|
81
|
+
|
|
82
|
+
- No issue spotted. Prose/docs-only. User fenced the scope ("just this one line").
|
|
83
|
+
- The issue is the task itself — then it's just the task (no ladder needed).
|
|
84
|
+
|
|
85
|
+
## See also
|
|
86
|
+
|
|
87
|
+
- [`minimal-safe-diff`](minimal-safe-diff.md) — the carve-out above is bounded by it; everything outside the five conditions stays note+ask.
|
|
88
|
+
- [`broken-access-control`](broken-access-control.md) — the security category + the live-exposure carve-out.
|
|
89
|
+
- [`prefer-enums-over-literals`](prefer-enums-over-literals.md) — an instance of this ladder (enum literals).
|
|
90
|
+
- [`source-discovery-gate`](source-discovery-gate.md) — establishing the verified project version before modernizing.
|
|
91
|
+
- [`scope-control`](scope-control.md), [`commit-policy`](commit-policy.md) — PR/commit creation stays permission-gated.
|
|
92
|
+
- [`improve-before-implement`](improve-before-implement.md) — the pre-work challenge (before); this is the during/after remediation.
|
|
@@ -13,7 +13,7 @@ Four rules answer **"may the agent do this autonomously?"** Higher band wins.
|
|
|
13
13
|
|
|
14
14
|
| Band | Trigger | Canonical rule |
|
|
15
15
|
|---|---|---|
|
|
16
|
-
| **1. Hard Floor** | Prod-trunk merge · deploy · push · prod data/infra · whimsical bulk deletion · bulk-deletion or infra commit | [`non-destructive-by-default`](non-destructive-by-default.md) |
|
|
16
|
+
| **1. Hard Floor** | Prod-trunk merge · deploy · push · prod data/infra · whimsical bulk deletion · bulk-deletion or infra commit · irreversible external action (send · publish · post · purchase · submit) | [`non-destructive-by-default`](non-destructive-by-default.md) |
|
|
17
17
|
| **2. Permission Gate** | Git op · branch / PR / tag · architectural or library change · scope expansion | [`scope-control`](scope-control.md) |
|
|
18
18
|
| **3. Commit Default** | About to commit — never, unless one of four exceptions fires | [`commit-policy`](commit-policy.md) |
|
|
19
19
|
| **4. Trivial-vs-Blocking** | Routine workflow question — act or ask | [`autonomous-execution`](autonomous-execution.md) |
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: "auto"
|
|
3
|
+
tier: "2a"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
description: "Endpoint/query returning user or tenant data — authenticated ≠ authorized; enforce a server-derived ownership/tenant/role check + the three negative tests (401/non-owner/cross-tenant)"
|
|
6
|
+
triggers:
|
|
7
|
+
- intent: "returning user or tenant data"
|
|
8
|
+
- intent: "adding an endpoint that reads a record by id"
|
|
9
|
+
- keyword: "endpoint"
|
|
10
|
+
- keyword: "route"
|
|
11
|
+
- keyword: "authorization"
|
|
12
|
+
- keyword: "tenant"
|
|
13
|
+
- keyword: "IDOR"
|
|
14
|
+
- keyword: "session"
|
|
15
|
+
- keyword: "login"
|
|
16
|
+
- keyword: "controller"
|
|
17
|
+
- keyword: "findById"
|
|
18
|
+
routes_to:
|
|
19
|
+
- "skill:authz-review"
|
|
20
|
+
- "skill:threat-modeling"
|
|
21
|
+
- "skill:multi-tenancy"
|
|
22
|
+
workspaces: [engineering]
|
|
23
|
+
packs: [engineering-base]
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Broken Access Control
|
|
27
|
+
|
|
28
|
+
The most common — and most damaging — failure in real systems and AI-written code: log in as one user, see another user's data. **Authenticated ≠ authorized.** The login check passes so the endpoint *feels* protected; the per-object ownership/tenant check is a separate line devs and AI omit — especially when the object id comes from the request. OWASP Web #1 (A01:2021) and API #1 (BOLA/IDOR), trivially scriptable, and a recurring real-world breach class — e.g. First American Financial (885M documents exposed via sequential ids with no ownership check).
|
|
29
|
+
|
|
30
|
+
## The Iron Law
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
AUTHENTICATED IS NOT AUTHORIZED. EVERY REQUEST-SUPPLIED ID/FILTER GETS A
|
|
34
|
+
SERVER-DERIVED OWNERSHIP/TENANT CHECK BEFORE ANY DATA IS RETURNED.
|
|
35
|
+
NO DATA-RETURNING ENDPOINT IS DONE WITHOUT THREE NEGATIVE TESTS:
|
|
36
|
+
UNAUTHENTICATED → 401 · NOT-OWNER → 403/404 · CROSS-TENANT → 403/404 (404 HIDES EXISTENCE).
|
|
37
|
+
ROLE DECIDES WHICH FIELDS — THE SERVER STRIPS THEM. THE FRONTEND HIDING A FIELD IS NOT PROTECTION.
|
|
38
|
+
A LIVE CROSS-USER EXPOSURE IS A NOTIFIABLE BREACH — NEVER SILENTLY PATCH IT.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Non-optional controls (every data-returning surface)
|
|
42
|
+
|
|
43
|
+
- **Server-derived principal.** Ownership/tenant derived from the session/token, **never** from a request header/param/body the caller controls. A matching id in the request is not authorization.
|
|
44
|
+
- **Ownership/tenant check on every request id** before returning data. `findById(params.id)` with no `where owner/tenant = currentPrincipal` is the canonical bug.
|
|
45
|
+
- **Tenant-scoped by construction.** Every query on a tenant table carries the tenant predicate; prefer a base scope that injects it so a forgotten clause can't leak. (DSGVO: Mandantentrennung / Trennungskontrolle is a required TOM — a missing `tenant_id` filter is a compliance failure, not just a bug.)
|
|
46
|
+
- **Response minimization.** Return only entitled fields — no `SELECT *` / full-object serialization leaking PII "because the model has it" (Art. 25(2)).
|
|
47
|
+
- **Property-level authz (BOPLA / mass assignment).** Reject `role` / `user_id` / `tenant_id` / `is_admin` from the request body; explicit field allow-list, never whole-body binding.
|
|
48
|
+
- **Role/field-level output filtering (vertical BOPLA).** *Which* fields a principal receives depends on role + business rules — a driver role must not receive `price`; an office role must not receive the boss's `offer`. Serialize via a **role-scoped output DTO**, never the raw model / `to_json()`; sensitive fields (`price`/`cost`/`margin`/`salary`/`discount`/`offer`/internal notes) default-deny per role.
|
|
49
|
+
- **No guessable public ids** on sensitive resources — UUID/ULID, not sequential integers (turns one bug into full-DB enumeration).
|
|
50
|
+
|
|
51
|
+
## The three negative tests — the completion gate
|
|
52
|
+
|
|
53
|
+
A protected endpoint is **not complete** until these exist and pass (happy-path 200 alone is the tautology this rule stops):
|
|
54
|
+
|
|
55
|
+
1. **unauthenticated → 401** (no token).
|
|
56
|
+
2. **authenticated but not the owner → 403/404** (user A requests user B's object with A's token).
|
|
57
|
+
3. **cross-tenant → 403/404** (a principal in tenant A requests tenant B's resource; prefer 404 on sensitive resources — a hard 403 confirms the id exists in another tenant).
|
|
58
|
+
|
|
59
|
+
Small + task-aligned gap in code you touched → add the check **and** its negative test in the same commit (auto-fix boundary in `active-remediation`). Bigger → surface it.
|
|
60
|
+
|
|
61
|
+
## Role-based field-level access — both directions (vertical BOPLA + BFLA)
|
|
62
|
+
|
|
63
|
+
Object-ownership (above) is *horizontal* (may this principal touch this record). This is *vertical*: which **fields** and which **functions** a role may reach — the maintainer's case (driver ≠ price, office worker ≠ boss's offer). Depends on role + business rules, and **the server is the only boundary** — the frontend hiding a field does not protect it: the raw JSON on the wire is readable via `curl`/DevTools/a proxy (3Fun leak: a privacy toggle filtered only in the app; the server returned every user's location to a direct query).
|
|
64
|
+
|
|
65
|
+
- **Read side (CWE-213 / Excessive Data Exposure).** Build the response from a **role-scoped output DTO** — never serialize the ORM model directly. Sensitive fields default-deny per role. A client `?fields=` selection is ergonomics, not authorization — intersect it server-side with the role's read-allowlist.
|
|
66
|
+
- **Write side (CWE-915).** Bind input through a **role-scoped write-allowlist** — never the raw body. Read-allowed ≠ write-allowed: a role may *see* `status` but not *set* `status:"approved"`; two independent sets.
|
|
67
|
+
- **Function level (BFLA, OWASP API #5).** A role must not reach a function/verb reserved for a higher role. Gate **every mutating verb** (POST/PUT/PATCH/DELETE), deny-by-default — not just the GET you were asked about. The admin button being hidden is not a control.
|
|
68
|
+
- **Nested / GraphQL.** Field-level checks propagate to nested selections; a low-role token selecting a restricted field gets null/error, not data.
|
|
69
|
+
- **Negative tests, per (role × sensitive field), both directions:** lower-role token → the field is **absent from the raw response body** (assert the body, not the UI); lower-role setting it on input → rejected/ignored, **verified by re-reading the persisted record**; every privileged verb → 403 for lower roles (403/404 on sensitive object reads).
|
|
70
|
+
|
|
71
|
+
## Defense-in-depth (so one miss can't leak)
|
|
72
|
+
|
|
73
|
+
Stack ≥2 independent layers on sensitive data — query-level ownership scoping, a centralized default-deny policy layer (a route with no policy is denied, not silently open), and DB row-level security as the backstop for a forgotten `WHERE tenant_id`. The RLS / policy-layer configuration, the per-role authz matrix, and the audit greps live once in [`authz-review`](../skills/authz-review/SKILL.md).
|
|
74
|
+
|
|
75
|
+
## GDPR / DSGVO — data protection by design
|
|
76
|
+
|
|
77
|
+
A cross-user leak violates **Art. 5(1)(f)** (integrity & confidentiality — a principle → higher fine tier), **Art. 25** (by design/default: default-deny + least privilege + response minimization), and **Art. 32** — which also **mandates a process for regularly testing effectiveness** of these controls (untested authz is a direct Art. 32 gap; hence the three negative tests are non-optional). A discovered live exposure is a **notifiable breach** (Art. 33, 72 h from discovery) — surface as "notify + remediate", never a silent patch. Data-protection context, not legal advice → `privacy-review`, `domain-safety-pii`.
|
|
78
|
+
|
|
79
|
+
## Backstop greps (authoring-time)
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Record fetched by request id with no owner/tenant predicate nearby (high-noise — a hit means read the line, not auto-fix)
|
|
83
|
+
rg -n '(findById|find|findOne|get)\(\s*(req\.|request\.|params\.|\$request|\$id)'
|
|
84
|
+
# Client-supplied tenant/user hint used as the scope (should come from the session)
|
|
85
|
+
rg -n '(tenant|tenantId|user_id|userId)\s*=\s*(req|request|params|headers|query)\.'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The full authoring-time grep set (mass-assignment, raw-serialization, secrets) lives once in [`ai-code-blindspots`](../skills/ai-code-blindspots/SKILL.md).
|
|
89
|
+
|
|
90
|
+
A hit means read that line — is the ownership/tenant check present? Some are safe (already scoped); none should ship unchecked.
|
|
91
|
+
|
|
92
|
+
## When it fires
|
|
93
|
+
|
|
94
|
+
Writing/modifying any endpoint, route, query, or serializer returning user- or tenant-owned data, or any auth/session/login/tenant path.
|
|
95
|
+
|
|
96
|
+
## When NOT to fire
|
|
97
|
+
|
|
98
|
+
- Public, non-user-specific data with no principal concept (public docs page, health check with no data).
|
|
99
|
+
- Prose/docs/config-only edits.
|
|
100
|
+
|
|
101
|
+
## See also
|
|
102
|
+
|
|
103
|
+
- [`authz-review`](../skills/authz-review/SKILL.md) — the 6-stage per-entrypoint chain, defense-in-depth, authz matrix, negative-test contract.
|
|
104
|
+
- [`security-sensitive-stop`](security-sensitive-stop.md) — threat-model BEFORE editing an auth/tenant path (fires first).
|
|
105
|
+
- [`active-remediation`](active-remediation.md) — the fix-now/ask/follow-up ladder + the live-exposure safety carve-out.
|
|
106
|
+
- [`threat-modeling`](../skills/threat-modeling/SKILL.md), [`multi-tenancy`](../skills/multi-tenancy/SKILL.md), [`ai-code-blindspots`](../skills/ai-code-blindspots/SKILL.md), [`privacy-review`](../skills/privacy-review/SKILL.md).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: "auto"
|
|
3
|
+
tier: "2b"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
description: "Multi-step or continuation replies carry a red thread — state the goal once, tie each turn back to it, name what changed since last turn, close the loop with one end-summary"
|
|
6
|
+
triggers:
|
|
7
|
+
- intent: "producing a multi-step or continuation reply"
|
|
8
|
+
- intent: "reporting progress on ongoing work"
|
|
9
|
+
workspaces: [agent-config-maintainer, construction, engineering, finance, founder, gtm, legal-review-prep, ops, product, small-business]
|
|
10
|
+
packs: [meta]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Communication Through-Line
|
|
14
|
+
|
|
15
|
+
The user follows the work through your replies. A multi-step task whose replies jump between sub-topics with no visible thread forces the user to re-derive where things stand every turn. Give the work a **red thread**: a followable narrative from goal to result. Session-spanning coherence — distinct from single-reply brevity (`direct-answers`) and option formatting (`user-interaction`), which it never overrides.
|
|
16
|
+
|
|
17
|
+
## The Iron Law
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
STATE THE GOAL ONCE. TIE EACH TURN BACK TO IT. NAME WHAT CHANGED SINCE THE LAST TURN.
|
|
21
|
+
CLOSE THE LOOP WITH ONE END-SUMMARY. NEVER MAKE THE USER RE-DERIVE WHERE THINGS STAND.
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Checkable behaviors
|
|
25
|
+
|
|
26
|
+
- **Anchor once.** At the start of a multi-step task, state the goal/plan in one line (not re-stated every turn).
|
|
27
|
+
- **Each turn locates itself.** A mid-task reply says, in a few words, where it sits in the plan ("step 3/5", "now the downstream wiring") — so the user never scrolls up to orient.
|
|
28
|
+
- **Name the delta.** On a continuation, say what changed since the last turn (what landed, what's next) rather than restating everything.
|
|
29
|
+
- **Close the loop.** A work reply ends with ONE end-summary mapping back to the stated goal — reached or not, what remains. (Same single end-summary `direct-answers` requires; do not add a second.)
|
|
30
|
+
- **Signal pivots.** If the work changes direction (new sub-task, interrupt, blocker), say so explicitly — a silent topic switch breaks the thread.
|
|
31
|
+
|
|
32
|
+
## When NOT to over-apply
|
|
33
|
+
|
|
34
|
+
- A one-shot answer or trivial reply needs no plan-anchor or end-summary (`direct-answers` brevity wins).
|
|
35
|
+
- Don't narrate for its own sake — the thread is orientation, not play-by-play (respect `personal.play_by_play`).
|
|
36
|
+
- Never let the through-line inflate a reply past what the task needs; brevity and the one-recommendation rule win on conflict.
|
|
37
|
+
|
|
38
|
+
## See also
|
|
39
|
+
|
|
40
|
+
- [`direct-answers`](direct-answers.md) — brevity + the single end-summary this closes the loop with.
|
|
41
|
+
- [`user-interaction`](user-interaction.md) — the one-recommendation format; through-line never adds a competing summary.
|
|
42
|
+
- [`language-and-tone`](language-and-tone.md) — the thread is in the user's language.
|