Skip to content

Bug: marti_archive: false is silently dropped by from_geojson() #140

Description

Summary

from_geojson() cannot produce a CoT with an explicit <marti archive="false">. The property that
controls it, feature.properties.marti_archive, is read with truthy checks in three places instead of
!== undefined, so false is indistinguishable from "not set" and the attribute is silently omitted.

// lib/parser/from_geojson.ts:103-113
if (feature.properties.dest || feature.properties.marti_archive) {
    cot.event.detail.marti = {};

    if (feature.properties.marti_archive) {
        cot.event.detail.marti._attributes = { archive: true };
    }

    if (feature.properties.dest) {
        const dest = !Array.isArray(feature.properties.dest) ? [ feature.properties.dest ] : feature.properties.dest;
        cot.event.detail.marti.dest = dest.map((dest) => {
            return { _attributes: { ...dest } };
        });
    }
}

marti_archive: true works correctly. marti_archive: false produces byte-identical output to
marti_archive: undefined (no dest set): no <marti> element is emitted at all, so nothing about
archiving is asserted one way or the other on the wire.

Reproduction

import { from_geojson } from '@tak-ps/node-cot/lib/parser/from_geojson.js';

const cot = await from_geojson({
    type: 'Feature',
    properties: { marti_archive: false },
    geometry: { type: 'Point', coordinates: [0, 0] },
});

console.log(cot.raw.event.detail.marti); // undefined - expected { _attributes: { archive: false } }

Why this matters

archive on <marti> is not cosmetic. On TAK Server, SubmissionService reads it per-message
(String archive = ((Element) marti).attributeValue("archive")) and sets a context flag that
RepositoryService checks before writing the message to the retention database. The message-level tag
takes precedence over the receiving input's own archive default — it's read by a listener added after
the input-level one, so it always overwrites. That means a client is fully able to say "don't retain this
specific CoT" independent of how the input is configured, provided it can actually put archive="false"
on the wire
— which from_geojson() currently prevents.

Concretely: a consumer wants to mark certain generated CoTs as non-archival on an input whose default is
archive="true". There is no way to express that through this library. Every marti_archive value except
true produces the same (archived) result.

Fix

Two small changes, both backward compatible — true and undefined behave identically to today; only
false changes, from a no-op to doing what it already claims to do:

-    if (feature.properties.dest || feature.properties.marti_archive) {
+    if (feature.properties.dest || feature.properties.marti_archive !== undefined) {
         cot.event.detail.marti = {};

-        if (feature.properties.marti_archive) {
-            cot.event.detail.marti._attributes = { archive: true };
+        if (feature.properties.marti_archive !== undefined) {
+            cot.event.detail.marti._attributes = { archive: feature.properties.marti_archive };
         }

The outer gate needs to widen too — otherwise marti_archive: false with no dest still fails to create
the <marti> element at all, since the || never becomes true.

I verified the underlying XML serialization has no issue with a false attribute value —
@tak-ps/xml-js produces <marti archive="false"/> correctly; the bug is purely in the truthy checks
above, not downstream.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions