Help with Instance Tagging

so i have a script here that checks to see if a placeholder part is within the field of view of an object. it works reasonably well, but when i try to use CollectionService to check if the primary parts of tagged models are in the field of view, it does not function. i have no idea why.

(for context, this is a LocalScript inside of a tool)

i have tried to remedy this problem with a couple of solutions, none of which worked:
  • looping through every instance with the tag, checked to see if the instance was a model, and called the “check_fov()” function, using the primary part as the second argument.

  • using a table to loop through every tagged instance, checking to see if they were a valid instance, and then checking the field of view.

here is the script that works with the placeholder object:

-- services
local run_service = game:GetService("RunService")
local collection_service = game:GetService("CollectionService")

-- other variables
local phone_equipped = false
local enemy_tag = "invisible_enemy"
local visibility_tag = "is_visible"
local rbx_connection

local npc_fov = 70
local transparency_threshold = 0.5

function has_obstacle(start_position, end_position, filter_instances, fov_angle)
	local ray_params = RaycastParams.new()
	ray_params.FilterType = Enum.RaycastFilterType.Exclude
	ray_params.FilterDescendantsInstances = filter_instances

	local hit_result = workspace:Raycast(start_position, end_position - start_position, ray_params)
	if hit_result and hit_result.Instance and hit_result.Instance.Transparency >= transparency_threshold then
		return false
	end
	return hit_result ~= nil
end

function check_fov(sight_part, target_obj, npc_fov)
	if not phone_equipped then
		return
	end

	local field_of_view_angle = math.rad(npc_fov)

	local npc_to_character = (target_obj.Position - sight_part.Position).Unit
	local npc_look_vector = sight_part.CFrame.LookVector
	local dot_product = npc_to_character:Dot(npc_look_vector)

	if dot_product > math.cos(field_of_view_angle / 2) and not has_obstacle(sight_part.Position, target_obj.Position, {sight_part, target_obj}, field_of_view_angle) then
		sight_part.Color = Color3.fromRGB(255, 76, 76)
		sight_part:SetAttribute("status", "i can see an enemy!")
		collection_service:AddTag(target_obj, visibility_tag)
	else
		sight_part.Color = Color3.fromRGB(66, 255, 151)
		sight_part:SetAttribute("status", "i cannot see any enemies")
		collection_service:RemoveTag(target_obj, visibility_tag)
	end
end

local tool = script.Parent
local handle = tool:WaitForChild("Handle")
local player_cube = workspace:WaitForChild("main_cube")

tool.Equipped:Connect(function()
	phone_equipped = true

	rbx_connection = run_service.PreSimulation:Connect(function()
		check_fov(handle, player_cube, npc_fov)
	end)
end)

tool.Unequipped:Connect(function()
	if rbx_connection then
		rbx_connection:Disconnect()
	end

	phone_equipped = false
	-- forcibly change statuses
	handle:SetAttribute("status", "i cannot see any enemies")
	collection_service:RemoveTag(player_cube, visibility_tag)
end)