docs(coverage): say "declared domain or IP root" for auto-scope roots (F8)

declaredScopeFilter treats only root_domain, subdomain, ip and cidr rows as
roots. A declared company, icp or keyword row does not make an auto row
count, but the add_task_scope description said "a goal target or scope
added here or manually". The description and the code comments now say
"declared domain or IP root" and name the kinds that act as roots.
This commit is contained in:
BaaRai
2026-11-12 09:38:52 +01:00
parent 37fab18809
commit cd8a830b53
5 changed files with 16 additions and 15 deletions
+4 -4
View File
@@ -365,9 +365,9 @@ func (t *ToolSet) insertAssets() actool.CoreTool {
// type; side-effect-derived assets don't go through here, so the scope
// doesn't widen blindly. No-op when taskID=0.
// The item may come from target content, so this auto row counts toward
// coverage and list_untested_assets only under a declared root (a
// non-auto row: goal target, add_task_scope or manual scope, see
// db.declaredScopeFilter). task_scope is
// coverage and list_untested_assets only under a declared domain or IP
// root (a non-auto root_domain/subdomain/ip/cidr row: goal target,
// add_task_scope or manual scope, see db.declaredScopeFilter). task_scope is
// coverage scope, not enforced: nothing blocks a call outside it.
// Independent of the coverage switch: task_scope is the task's range
// boundary (the filter basis for list/query); the coverage switch only
@@ -454,7 +454,7 @@ func (t *ToolSet) addTaskScope() actool.CoreTool {
"add_task_scope",
"Add test scope to [this task] — "+taskScopeLabel+": it is the denominator of asset test coverage; nothing blocks a tool call outside it.\n"+
"Supported kinds: company (all assets under the company) / root_domain (a whole root domain, including all subdomains) / subdomain (a single precise subdomain) / ip / cidr / icp / keyword.\n"+
"Note: hosts a worker touches one by one are [automatically] added to the scope by the system (precise subdomains), but they count toward coverage and list_untested_assets only under a declared root (a goal target or scope added here or manually); this tool is for [deliberate widening] — pulling in a whole root domain / whole company, or adding a specific subdomain/IP.\n"+
"Note: hosts a worker touches one by one are [automatically] added to the scope by the system (precise subdomains), but they count toward coverage and list_untested_assets only under a declared domain or IP root (a root_domain/subdomain/ip/cidr goal target or scope added here or manually; a company/icp/keyword entry is not such a root); this tool is for [deliberate widening] — pulling in a whole root domain / whole company, or adding a specific subdomain/IP.\n"+
"value: for company pass the company name or id (the company must already exist); for root_domain/subdomain pass a domain; for ip/cidr pass an IP or range; for icp/keyword pass an ICP number or company keyword.\n"+
"Always give reason explaining the basis (auditable). Use the entries array for multiple entries.",
obj(map[string]any{
+1 -1
View File
@@ -455,7 +455,7 @@ func (w *Worker) execute(ctx context.Context, name string, taskID int64, as *db.
// ON CONFLICT DO NOTHING + the uq_task_scope unique index guarantee no
// duplicate additions; reruns/retries are idempotent no-ops as well.
// asset_ids can trace back to target content, so these auto rows count
// toward coverage only under a declared root (db.declaredScopeFilter);
// toward coverage only under a declared domain or IP root (db.declaredScopeFilter);
// coverage scope, not enforced.
// When the asset-coverage feature is off, the test scope (denominator) is no
// longer accumulated.
+1 -1
View File
@@ -760,7 +760,7 @@ WHERE t.active_llm_profile_id IS NULL
-- side-effect-derived assets do not enter the scope (the hook sits at the handler top level; derivation is internal to the db layer).
-- Agent-filled (source='agent'): add_task_scope adds company/root_domain/subdomain/ip.
-- Coverage = among assets matching active rows (denominator), the share anchored by fact nodes (numerator).
-- An auto row is active only under a declared (non-auto) root of the same task (declaredScopeFilter in task_scope.go).
-- An auto row is active only under a declared (non-auto) domain or IP root of the same task (declaredScopeFilter in task_scope.go).
CREATE TABLE IF NOT EXISTS task_scope (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
+1 -1
View File
@@ -121,7 +121,7 @@ ORDER BY CASE WHEN ts.task_id=$1 THEN 0 ELSE 1 END, ts.id`, taskID)
// TaskCoverageWithSources computes one coverage view over the union of the
// current task and its direct sources: source scopes and fact-anchored assets
// extend the denominator, while fact anchors count as tested. Auto scope rows
// count only under a declared root (declaredScopeFilter), and an intent anchor
// count only under a declared domain or IP root (declaredScopeFilter), and an intent anchor
// alone (insert_assets, asset_ids) no longer adds an asset. No row is copied.
func (s *AssetStore) TaskCoverageWithSources(taskID int64) (*Coverage, error) {
cov := &Coverage{ByType: []CoverageByType{}}
+9 -8
View File
@@ -14,7 +14,8 @@ import (
// conservative, one per explicitly-inserted asset), from the add_task_scope tool
// (source='agent': the goal targets the decomposer registers, or planner widening),
// or from the operator (source='manual'). An auto row counts toward coverage only
// under a declared (non-auto) root, see declaredScopeFilter.
// under a declared domain or IP root (a non-auto root_domain/subdomain/ip/cidr row),
// see declaredScopeFilter.
type TaskScope struct {
ID int64 `json:"id"`
TaskID int64 `json:"task_id"`
@@ -109,7 +110,7 @@ func (s *AssetStore) upsertTaskScope(ts TaskScope) error {
// never from a db-layer side effect (linkHostAssets), so derived assets do not blindly expand the scope.
// Rule: scope granularity follows the asset's own type. taskID<=0 → no-op.
// The asset comes from target-derived content, so the row counts toward coverage and
// list_untested_assets only under a declared root (declaredScopeFilter).
// list_untested_assets only under a declared domain or IP root (declaredScopeFilter).
func (s *AssetStore) AddAutoScope(taskID int64, assetType, domain, rawURL, ip string) error {
if taskID <= 0 {
return nil
@@ -280,7 +281,7 @@ type CoverageByType struct {
// Coverage is a task's rough asset test coverage — a reference figure for the agent,
// NOT a precise metric. Denominator = assets matching a declared task_scope row or an
// auto row under a declared root (declaredScopeFilter); Tested = those anchored to at
// auto row under a declared domain or IP root (declaredScopeFilter); Tested = those anchored to at
// least one fact node in the exploration.
type Coverage struct {
Enabled bool `json:"enabled"` // whether the asset coverage feature is on; when false the other fields are zero
@@ -307,11 +308,11 @@ func (s *AssetStore) CoverageEnabled(taskID int64) bool {
// declaredScopeFilter keeps a task_scope row ts in the coverage match only when it is
// declared (source<>'auto': goal targets registered through add_task_scope, or manual
// scope) or when it is an auto row under a root the same task declared: the same or
// a deeper domain of a declared root_domain/subdomain, or a net inside a declared
// ip/cidr. Auto rows come from target-derived content (insert_assets, intent
// asset_ids), so anything else stays out of the coverage denominator and
// list_untested_assets.
// scope) or when it is an auto row under a domain or IP root the same task declared:
// the same or a deeper domain of a declared root_domain/subdomain, or a net inside a
// declared ip/cidr. A declared company, icp or keyword row is not a root here. Auto
// rows come from target-derived content (insert_assets, intent asset_ids), so
// anything else stays out of the coverage denominator and list_untested_assets.
const declaredScopeFilter = `(ts.source <> 'auto' OR EXISTS (
SELECT 1 FROM task_scope d
WHERE d.task_id = ts.task_id AND d.source <> 'auto' AND (